Уничтожение объектов в C++
когда именно объекты уничтожаются в C++, и что это значит? Должен ли я уничтожать их вручную, так как нет сборщика мусора? Как возникают исключения?
(Примечание: это должно быть запись в C++ FAQ переполнения стека. Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация на meta, которая начала все это было бы место, чтобы сделать это. Ответы на этот вопрос контролируется в в C++ чат, где идея FAQ началась в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)
2 ответов
в следующем тексте, я буду судить между область, время разрушения которого статически определяется их охватывающей областью (функциями, блоками, классами, выражениями) и динамические объекты, точное время разрушения которого обычно не известно до времени выполнения.
в то время как семантика уничтожения объектов класса определяется деструкторами, уничтожение скалярного объекта всегда является no-op. В частности, разрушая указатель переменная не уничтожить ссылающиеся на заданный.
область
автоматические объекты
автоматические объекты (обычно называемые "локальные переменные") разрушаются, в обратном порядке их определения, когда поток управления покидает область их определения:
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
если исключение выдается во время the строительство один из Foo
's подобъектов, то все его ранее построенные подобъекты будут уничтожены до распространения исключения. The 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