Уничтожение объектов в C++
когда именно уничтожены объекты в C++, и что это значит? Должен ли я уничтожить их вручную, так как нет сборщика мусора? Как исключения вступают в игру?
(Примечание:это должно быть запись в C++ FAQ Stack Overflow. Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация на meta, которая начала все это было бы место, чтобы сделать это. Ответы на этот вопрос отслеживаются в в C++ чат, где идея FAQ началась в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)
2 ответа:
в следующем тексте, я буду судить между область, время разрушения которых статически определяется их охватывающей областью (функции, блоки, классы, выражения), и динамические объекты, чье точное время разрушения обычно не известно до времени выполнения.
в то время как семантика уничтожения объектов класса определяется деструкторы, разрушение скалярный объект всегда нет. Конкретно, разрушение переменной указателя делает не уничтожить ссылающиеся на заданный.
область
автоматические объекты
автоматические объекты (обычно называемые "локальные переменные") разрушаются, в обратном порядке их определения, когда поток управления покидает область их определения:
void some_function() { Foo a; Foo b; if (some_condition) { Foo y; Foo z; } <--- z and y are destructed here } <--- b and a are destructed here
если во время выполнения функции возникает исключение, то все ранее построенные автоматические объекты уничтожаются раньше исключение передается вызывающему объекту. Этот процесс называется стек разматывать. Во время размотки стека никакие дополнительные исключения не могут оставить деструкторы вышеупомянутых ранее построенных автоматических объектов. В противном случае функция
std::terminate
называется.это приводит к одному из самых важных принципов в C++:
деструкторы никогда не должны бросать.
нелокальный статический объекты
статические объекты, определенные в области пространства имен (обычно называемые "глобальными переменными"), и статические элементы данных разрушаются в обратном порядке их определения после выполнения
main
:struct X { static Foo x; // this is only a *declaration*, not a *definition* }; Foo a; Foo b; int main() { } <--- y, x, b and a are destructed here Foo X::x; // this is the respective definition Foo y;
обратите внимание, что относительный порядок построения (и разрушения) статических объектов, определенных в различных единицах перевода, не определен.
если исключение покидает деструктор статического объекта, функция
std::terminate
is называемый.локальные статические объекты
статические объекты, определенные внутри функции создается, когда (и если) поток управления проходит через их определение впервые.1 Они разрушаются в обратном порядке после выполнения
main
:Foo& get_some_Foo() { static Foo x; return x; } Bar& get_some_Bar() { static Bar y; return y; } int main() { get_some_Bar().do_something(); // note that get_some_Bar is called *first* get_some_Foo().do_something(); } <--- x and y are destructed here // hence y is destructed *last*
если исключение покидает деструктор статического объекта, функция
std::terminate
называется.1: это чрезвычайно упрощенная модель. Этот детали инициализации статических объектов на самом деле намного сложнее.
субобъекты базового класса и субобъекты-члены
когда поток управления покидает тело деструктора объекта, его субобъекты-члены (также известные как "элементы данных") разрушаются в обратном порядке их определения. После этого его субобъекты базового класса разрушаются в обратном порядке списка базовых спецификаторов:
class Foo : Bar, Baz { Quux x; Quux y; public: ~Foo() { } <--- y and x are destructed here, }; followed by the Baz and Bar base class subobjects
если исключение возникает во время элемент строительство один из
Foo
's подобъектов, то все его ранее построенные подобъекты будут уничтожены до распространения исключения. ЭлементFoo
деструктор, с другой стороны, будет не быть казнен, так как
деструктор объекта вызывается автоматически, когда срок службы объекта заканчивается и он уничтожается. Обычно вы не должны вызывать его вручную.
мы будем использовать этот объект в качестве примера:
class Test { public: Test() { std::cout << "Created " << this << "\n";} ~Test() { std::cout << "Destroyed " << this << "\n";} Test(Test const& rhs) { std::cout << "Copied " << this << "\n";} Test& operator=(Test const& rhs) { std::cout << "Assigned " << this << "\n";} };
есть три (четыре в C++11) различных типов объектов в C++ и тип объекта определяет срок службы объектов.
- статические объекты длительности хранения
- автоматические объекты продолжительности хранения
- динамические объекты длительности хранения
- (в C++11) объекты длительности хранения потоков
статические объекты длительности хранения
это самые простые и приравниваются к глобальным переменным. Продолжительность жизни этих объектов (как правило,) длительность применения. Они (обычно) строятся до того, как main вводится и уничтожается (в обратном порядке создания) после выхода из main.
Test global; int main() { std::cout << "Main\n"; } > ./a.out Created 0x10fbb80b0 Main Destroyed 0x10fbb80b0
Примечание 1: Есть два других типа статического объекта длительности хранения.
статические переменные-члены класса.
Эти для всех смысла и цели такие же как глобальные переменные по отоношению к продолжительности жизни.
статические переменные внутри функции.
это лениво, созданные статические объекты длительности хранения. Они создаются при первом использовании (в потокобезопасной усадьбе для C++11). Так же, как и другие статические объекты длительности хранения они уничтожаются, когда приложение концы.
порядок строительства/разрушения
- порядок построения внутри единицы компиляции четко определен и совпадает с объявлением.
- порядок построения между единицами компиляции не определен.
- порядок разрушения является точным обратным порядку строительства.
автоматическая продолжительность хранения объектов
это наиболее распространенный тип объектов, и что вы должны использовать 99% времени.
это три основных типа автоматических переменных:
- локальные переменные внутри функции/блок
- переменные-члены внутри класса/массив.
- временные переменные.
Локальные Переменные
при выходе из функции / блока все переменные, объявленные внутри этой функции/блока, будут уничтожены (в обратном порядке создание.)
int main() { std::cout << "Main() START\n"; Test scope1; Test scope2; std::cout << "Main Variables Created\n"; { std::cout << "\nblock 1 Entered\n"; Test blockScope; std::cout << "block 1 about to leave\n"; } // blockScope is destrpyed here { std::cout << "\nblock 2 Entered\n"; Test blockScope; std::cout << "block 2 about to leave\n"; } // blockScope is destrpyed here std::cout << "\nMain() END\n"; }// All variables from main destroyed here. > ./a.out Main() START Created 0x7fff6488d938 Created 0x7fff6488d930 Main Variables Created block 1 Entered Created 0x7fff6488d928 block 1 about to leave Destroyed 0x7fff6488d928 block 2 Entered Created 0x7fff6488d918 block 2 about to leave Destroyed 0x7fff6488d918 Main() END Destroyed 0x7fff6488d930 Destroyed 0x7fff6488d938
переменные-члены
срок службы переменных-членов привязан к объекту, которому он принадлежит. Когда срок службы владельцев заканчивается все его члены срок службы также заканчивается. Поэтому нужно смотреть на жизнь владельца, который подчиняется тем же правилам.
Примечание: члены всегда уничтожаются перед владельцем в обратном порядке создания.
- таким образом, для членов класса, они создаются в порядке декларация
и уничтожается в обратном порядке декларирования- таким образом, для членов массива они создаются в порядке 0-->top
и уничтожаются в обратном порядке сверху-->0временные переменные
это объекты, которые создаются в результате самовыражения, а не присваивается переменной. Временные переменные уничтожаются так же, как и другие автоматические переменные. Это просто, что конец их сферы действия-это конец сообщении в котором они созданы (обычно это ';').
std::string data("Text."); std::cout << (data + 1); // Here we create a temporary object. // Which is a std::string with '1' added to "Text." // This object is streamed to the output // Once the statement has finished it is destroyed. // So the temporary no longer exists after the ';'
Примечание: есть ситуации, когда жизнь временного может быть продлен.
Но это не имеет отношения к этой простой дискуссии. К тому времени вы поймете, что этот документ станет для вас второй натурой и до того, как он продлит жизнь временно, это не то, что вы хотите сделать.динамические объекты длительности хранения
эти объекты имеют динамический срок службы и создаются с
new
и уничтожен с вызовомdelete
.int main() { std::cout << "Main()\n"; Test* ptr = new Test(); delete ptr; std::cout << "Main Done\n"; } > ./a.out Main() Created 0x1083008e0 Destroyed 0x1083008e0 Main Done
для разработчиков, которые приходят из мусорных собранных языков, это может показаться странным (управление сроком службы вашего объекта). Но проблема не так плоха, как кажется. В C++ необычно использовать динамически выделенные объекты напрямую. Мы имеем объекты управления для того чтобы контролировать их продолжительность жизни.
самое близкое к большинству других собранных языков GC-это
std::shared_ptr
. Эта воля следите за количеством пользователей динамически создаваемого объекта и когда все они уйдут вызоветdelete
автоматически (я думаю об этом как о лучшей версии обычного объекта Java).int main() { std::cout << "Main Start\n"; std::shared_ptr<Test> smartPtr(new Test()); std::cout << "Main End\n"; } // smartPtr goes out of scope here. // As there are no other copies it will automatically call delete on the object // it is holding. > ./a.out Main Start Created 0x1083008e0 Main Ended Destroyed 0x1083008e0
объекты длительности хранения потоков