Уничтожение объектов в C++


когда именно уничтожены объекты в C++, и что это значит? Должен ли я уничтожить их вручную, так как нет сборщика мусора? Как исключения вступают в игру?

(Примечание:это должно быть запись в C++ FAQ Stack Overflow. Если вы хотите критиковать идею предоставления FAQ в этой форме, то публикация на meta, которая начала все это было бы место, чтобы сделать это. Ответы на этот вопрос отслеживаются в в C++ чат, где идея FAQ началась в первую очередь, поэтому ваш ответ, скорее всего, будет прочитан теми, кто придумал эту идею.)

2 59

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

объекты длительности хранения потоков