Уничтожение объектов в 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

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