#новичкам
Представляете, забыл выложить один важный пост из серии про недостатки std::make_shared. Затерялся он в пучине заметок. Исправляюсь.
В предыдущих сериях:
Кастомный new и delete
Непубличные конструкторы
Кастомные делитеры
А теперь поговорим про деаллокацию.
В этом посте мы рассказали о том, что std::make_shared выделяет один блок памяти под объект и контрольный блок. 1 аллокация вместо 2-х = большая производительность. Однако у монеты всегда две стороны.
Что происходит с объектом и памятью при работе с shared_ptr напрямую через конструктор?
Отдельно выделяется память и конструируется разделяемый объект с помощью явного вызова new, отдельно выделяется память и конструируется контрольный блок.
Деструктор разделяемого объекта и освобождение памяти для него происходит ровно в тот момент, когда счетчик сильных ссылок становится нулем. При этом контрольный блок остается живым до момента уничтожения последнего std::weak_ptr:
void operator delete(void *ptr) noexcept {
std::cout << "Global delete " << std::endl;
std::free(ptr);
}
class MyClass {
public:
~MyClass() {
std::cout << "Деструктор MyClass вызван.\n";
}
};
int main() {
std::weak_ptr<MyClass> weak;
{
std::shared_ptr<MyClass> shared(new MyClass());
weak = shared;
std::cout
<< "shared_ptr goes out of scope...\n";
} // Here shared is deleting
std::cout << "weak.expired(): " << weak.expired()
<< '\n';
weak.reset();
std::cout << "All memory has been freed!\n";
}
// OUTPUT:
// shared_ptr goes out of scope...
// Dtor MyClass called.
// Global delete
// weak.expired(): 1
// Global delete
// All memory has been freed!Мы переопределили глобальный delete, чтобы увидеть момент освобождения памяти на разных этапах.
Деструктор и delete для разделяемого объекта вызываются ровно в момент выхода объекта
shared из своего скоупа. Тем не менее weak_ptr жив, он знает, что объекта уже нет, но своим наличием продлевает время жизни контрольного блока. После ресета weak ожидаемо происходит деаллокация блока.Что же происходит при использовании std::make_shared? В какой момент освобождается вся выделенная память?
Только когда оба счетчика сильных и слабых ссылок будут равны нулю. То есть не осталось ни одного объекта std::shared_ptr и std::weak_ptr, которые указывают на объект. Тем не менее семантически разделяемый объект уничтожается при разрушении последней сильной ссылки:
void operator delete(void *ptr) noexcept {
std::cout << "Global delete " << std::endl;
std::free(ptr);
}
class MyClass {
public:
~MyClass() {
std::cout << "Деструктор MyClass вызван.\n";
}
};
int main() {
std::weak_ptr<MyClass> weak;
{
auto shared = std::make_shared<MyClass>();
weak = shared;
std::cout << "shared_ptr goes out of scope...\n";
} // shared уничтожается здесь
std::cout << "weak.expired(): " << weak.expired()
<< '\n'; // true
weak.reset();
std::cout << "All memory has been freed!\n";
}
// shared_ptr goes out of scope...
// Dtor MyClass called.
// weak.expired(): 1
// Global delete
// All memory has been freed!Отметьте вызов деструктора после выхода из скоупа, но при этом память еще не освобождается. Она делает это только после reset и уничтожении последней слабой ссылки.
Учитывайте эти особенности, когда используете weak_ptr(например в кэше или списках слушателей).
Consider both sides of the coin. Stay cool.
#cpp11 #memory