std::allocate_shared
#опытным
В одном из прошлых постов мы упоминали, что одним из недостатков std::make_shared является то, что с ней нельзя использовать кастомный менеджмент памяти.
Причиной является то, что для выделения памяти она использует глобальный new. Поэтому и для освобождения памяти должна использовать глобальный delete. Здесь нет места кастомщине.
Но что, если нам очень нужно по-особенному работать с памятью для объекта? Даже хотя бы просто отслеживать выделение и разрушение без влияния на глобальный delete?
Тут на помощью приходит функция std::allocate_shared. Первым аргументом она принимает аллокатор, который и будет ответственен за выделение и освобождение памяти.
Вот вам простой примерчик с простым STL-совместимым аллокатором, логирующим операции выделения и освобождения памяти:
Стандартом на аллокаторы накладываются определенные требования: нужно определить нужные алиасы, методы allocate и deallocate, структуру rebind и соответствующий конструктор копирования и операторы сравнения. Полный список требований можно прочитать тут.
По консольному выводу видно, что аллокатор выделяет немного больше памяти, чем должен занимать объект. Это сделано в том числе для того, чтобы хранить в выделенном куске еще и контрольный блок std::shared_ptr. Так что касаемо особенностей аллокации и деаллокации тут похожая ситуация с std::make_shared.
Аллокатор кстати хранится в том же контрольном блоке, так что информация о способе деаллокации берется оттуда.
Customize your solutions. Stay cool.
#cppcore #STL #memory
#опытным
В одном из прошлых постов мы упоминали, что одним из недостатков std::make_shared является то, что с ней нельзя использовать кастомный менеджмент памяти.
Причиной является то, что для выделения памяти она использует глобальный new. Поэтому и для освобождения памяти должна использовать глобальный delete. Здесь нет места кастомщине.
Но что, если нам очень нужно по-особенному работать с памятью для объекта? Даже хотя бы просто отслеживать выделение и разрушение без влияния на глобальный delete?
Тут на помощью приходит функция std::allocate_shared. Первым аргументом она принимает аллокатор, который и будет ответственен за выделение и освобождение памяти.
Вот вам простой примерчик с простым STL-совместимым аллокатором, логирующим операции выделения и освобождения памяти:
template <typename T>
class LoggingAllocator {
public:
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
template <typename U>
struct rebind {
using other = LoggingAllocator<U>;
};
LoggingAllocator() noexcept = default;
template <typename U>
LoggingAllocator(const LoggingAllocator<U> &) noexcept {}
pointer allocate(size_type n) {
size_type bytes = n * sizeof(T);
std::cout << "Allocating " << n << " elements ("
<< bytes << " bytes)\n";
return static_cast<pointer>(::operator new(bytes));
}
void deallocate(pointer p, size_type n) noexcept {
size_type bytes = n * sizeof(T);
std::cout << "Deallocating " << n << " elements ("
<< bytes << " bytes)\n";
::operator delete(p);
}
};
template <typename T, typename U>
bool operator==(const LoggingAllocator<T> &,
const LoggingAllocator<U> &) noexcept { return true; }
template <typename T, typename U>
bool operator!=(const LoggingAllocator<T> &,
const LoggingAllocator<U> &) noexcept { return false; }
class MyClass {
public:
MyClass(int value) : value(value) {
std::cout << "Constructed with " << value << "\n";
}
~MyClass() {
std::cout << "Destroyed with " << value << "\n";
}
void print() const {
std::cout << "Value: " << value << "\n";
}
private:
int value;
};
int main() {
LoggingAllocator<MyClass> alloc;
auto ptr = std::allocate_shared<MyClass>(alloc, 42);
ptr->print();
return 0;
}
// OUTPUT:
// Allocating 1 elements (24 bytes)
// Constructed with 42
// Value: 42
// Destroyed with 42
// Deallocating 1 elements (24 bytes)
Стандартом на аллокаторы накладываются определенные требования: нужно определить нужные алиасы, методы allocate и deallocate, структуру rebind и соответствующий конструктор копирования и операторы сравнения. Полный список требований можно прочитать тут.
По консольному выводу видно, что аллокатор выделяет немного больше памяти, чем должен занимать объект. Это сделано в том числе для того, чтобы хранить в выделенном куске еще и контрольный блок std::shared_ptr. Так что касаемо особенностей аллокации и деаллокации тут похожая ситуация с std::make_shared.
Аллокатор кстати хранится в том же контрольном блоке, так что информация о способе деаллокации берется оттуда.
Customize your solutions. Stay cool.
#cppcore #STL #memory
❤21👍13🔥9🤣1
Оператор, бороздящий просторы вселенной
#новичкам
В этом посте мы рассказали об одной фишке, которая может помочь при сравнении кастомных структур:
Однако иногда структуры требуется сравнивать и с помощью других операторов: >, ==, !=, >=, <=. В итоге полноценный набор операторов сравнения для Time выглядит так:
Попахивает зловонным бойлерплейтом.
Недавно увидел мем, где девочка 8-ми лет, которая изучает питон, спрашивает отца: "папа, а если компьютер знает, что здесь пропущено двоеточие, почему он сам не может его поставить?". И батя такой: "Я не знаю, дочка, я не знаю ...".
Здесь вот похожая ситуация. Компилятор же умеет сравнивать набор чисел в лексикографическом порядке. Какого хрена он не может сделать это за нас?
Начиная с С++20 может!
Теперь вы можете сказать компилятору, что вам достаточно простого лексикографического сравнения поле класса и пусть он сам его генерирует:
В отличие от специальных методов класса, компилятор не сгенерирует за нас эти операторы, если мы явно не попросим. Получается, что мы решили только полпроблемы и нам все равно нужно писать 6 скучных засоряющих код строчек. Хотелось бы один раз сказать, что нам нужны сразу все операторы.
Тут же нам на помощью приходит еще одна фича С++20 - трехсторонний оператор сравнения или spaceship operator. Теперь код выглядит так:
Spaceship потому что похож на космический корабль, имхо прям имперский истребитель из далекой-далекой.
Один раз определив этот оператор можно сравнивать объекты какими угодно операторами и это будет работать. Подробнее про применение будет в следующем посте.
Conquer your space. Stay cool.
#cppcore #cpp20
#новичкам
В этом посте мы рассказали об одной фишке, которая может помочь при сравнении кастомных структур:
struct Time {
int hours;
int minutes;
int seconds;
bool operator<(const Time& other) {
return std::tie(hours, minutes, seconds) < std::tie(other.hours, other.minutes, other.seconds);
}
};Однако иногда структуры требуется сравнивать и с помощью других операторов: >, ==, !=, >=, <=. В итоге полноценный набор операторов сравнения для Time выглядит так:
struct Time {
int hours;
int minutes;
int seconds;
bool operator<(const Time& other) const noexcept {
return std::tie(hours, minutes, seconds) < std::tie(other.hours, other.minutes, other.seconds);
}
bool operator==(const Time& other) const noexcept {
return std::tie(hours, minutes, seconds) == std::tie(other.hours, other.minutes, other.seconds);
}
bool operator<=(const Time& other) const noexcept { return !(other < *this); }
bool operator>(const Time& other) const noexcept { return other < *this; }
bool operator>=(const Time& other) const noexcept { return !(*this < other); }
bool operator!=(const Time& other) const noexcept { return !(*this == other); }
};Попахивает зловонным бойлерплейтом.
Недавно увидел мем, где девочка 8-ми лет, которая изучает питон, спрашивает отца: "папа, а если компьютер знает, что здесь пропущено двоеточие, почему он сам не может его поставить?". И батя такой: "Я не знаю, дочка, я не знаю ...".
Здесь вот похожая ситуация. Компилятор же умеет сравнивать набор чисел в лексикографическом порядке. Какого хрена он не может сделать это за нас?
Начиная с С++20 может!
Теперь вы можете сказать компилятору, что вам достаточно простого лексикографического сравнения поле класса и пусть он сам его генерирует:
struct Time {
int hours;
int minutes;
int seconds;
bool operator<(const Time& other) const = default;
bool operator==(const Time& other) const = default;
bool operator<=(const Time& other) const = default;
bool operator>(const Time& other) const = default;
bool operator>=(const Time& other) const = default;
bool operator!=(const Time& other) const = default;
};В отличие от специальных методов класса, компилятор не сгенерирует за нас эти операторы, если мы явно не попросим. Получается, что мы решили только полпроблемы и нам все равно нужно писать 6 скучных засоряющих код строчек. Хотелось бы один раз сказать, что нам нужны сразу все операторы.
Тут же нам на помощью приходит еще одна фича С++20 - трехсторонний оператор сравнения или spaceship operator. Теперь код выглядит так:
struct Time {
int hours;
int minutes;
int seconds;
// Один оператор вместо шести!
auto operator<=>(const Time& other) const = default;
};Spaceship потому что похож на космический корабль, имхо прям имперский истребитель из далекой-далекой.
Один раз определив этот оператор можно сравнивать объекты какими угодно операторами и это будет работать. Подробнее про применение будет в следующем посте.
Conquer your space. Stay cool.
#cppcore #cpp20
❤41👍22🔥12