Destroying objects in C ++

When exactly objects in C ++ are destroyed and what does it mean? Do I have to destroy them manually since there is no garbage collector? How do exceptions take effect?

<sub> (Note: this means writing to the C ++ stack overflow FAQ . If you want to criticize the idea of ​​providing the FAQ in this form, then posting on the meta that started it all would be the place to do it. Answers to this the question is tracked in C ++ chat , where the idea of ​​the FAQ started first, so your answer will most likely be read by those who came up with this idea.) Sub>

+59
c ++ c ++ - faq exception destructor object-lifetime
Jun 19 2018-11-11T00:
source share
2 answers

In the following text, I will distinguish between objects with a region whose destruction time is statically determined by their covering region (functions, blocks, classes, expressions) and dynamic objects, the exact destruction time of which is usually unknown until at runtime.

While the semantics of the destruction of class objects are determined by destructors, the destruction of a scalar object is always no-op. In particular, destroying a pointer variable does not destroy pointee.

Objects with a scope

auto objects

Automatic objects (usually called "local variables") are destroyed in the reverse order of their definition when the control flow goes beyond their definition:

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 

If an exception is thrown during the execution of the function, all previously constructed automatic objects are destroyed before the exception is passed to the caller. This process is called stack expansion. During the unwinding of the stack, no additional exceptions can leave the destructors of the aforementioned previously constructed automatic objects. Otherwise, the std::terminate function is called.

This leads to one of the most important principles in C ++:

Destructors should never throw.

nonlocal static objects

Static objects defined in a namespace area (commonly called "global variables") and static data members are destroyed in the reverse order of their definition after 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; 

Please note that the relative order of construction (and destruction) of static objects defined in different translation units is undefined.

If the exception leaves the static object's destructor, the std::terminate function is called.

local static objects

Static objects defined inside functions are constructed when (and if) the control flow passes through their definition for the first time. 1 They are destroyed in the reverse order after 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* 

If the exception leaves the static object's destructor, the std::terminate function is called.

1: This is an extremely simplified model. The details of initializing static objects are actually much more complicated.

base class subobjects and member subobjects

When the control flow leaves the body of the object’s destructor, its subobjects (also known as its “data members”) are destroyed in the reverse order of their definition. After that, the base class subobjects are destroyed in the reverse order of the base specifier-list:

 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 

If an exception occurs during the construction of one of the Foo subobjects, then all its previously constructed subobjects will be destroyed before the exception is propagated. On the other hand, the Foo destructor will not execute because the Foo object was never completely constructed.

Note that the body of the destructor is not responsible for the destruction of the data elements themselves. You only need to write a destructor if the data element is a handle to the resource that should be released when the object is destroyed (for example, a file, socket, database connection, mutex or a lot of memory).

array elements

Elements of the array are destroyed in descending order. If an exception is thrown during the construction of the nth element, elements n-1 to 0 are destroyed before the exception is propagated.

temporary objects

A temporary object is created when an expression of the class praleue class type is evaluated. The most striking example of a prvalue expression is a function call that returns an object by value, for example T operator+(const T&, const T&) . Under normal circumstances, a temporary object is destroyed when a complete expression that lexically contains the value of prvalue is fully evaluated:

 __________________________ full-expression ___________ subexpression _______ subexpression some_function(a + " " + b); ^ both temporary objects are destructed here 

The above function call some_function(a + " " + b) is a complete expression because it is not part of a larger expression (instead, it is part of an expression-expression). Therefore, all temporary objects that are created when evaluating subexpressions will be destroyed to a semicolon. There are two such temporary objects: the first during the first addition, the second during the second addition. The second temporary object will be destroyed before the first.

If an exception is thrown during the second add, the first first object will be destroyed properly before throwing the exception.

If the local link is initialized with the prvalue expression, the lifetime of the temporary object extends to the local link area, so you will not get a date link:

 { const Foo& r = a + " " + b; ^ first temporary (a + " ") is destructed here // ... } <--- second temporary (a + " " + b) is destructed not until here 

If a non-class prvalue expression is evaluated, the result is a value, not a temporary object. However, a temporary object will be created if prvalue is used to initialize the link:

 const int& r = i + j; 

Dynamic objects and arrays

In the next section, destroying X means "destroy X first and then free up base memory." Similarly, creating X means "first allocate enough memory, and then build X there."

dynamic objects

A dynamic object created with p = new Foo is destroyed via delete p . If you forget delete p , you will have a resource leak. You should never try to do one of the following, as they all lead to undefined behavior:

  • destroy a dynamic object via delete[] (note the square brackets), free or any other means
  • destroy a dynamic object several times
  • access to a dynamic object after its destruction

If an exception occurs during the construction of a dynamic object, the underlying memory is freed before the exception is thrown. (The destructor will not be executed until the memory is freed, because the object was never completely constructed.)

dynamic arrays

A dynamic array created using p = new Foo[n] is destroyed via delete[] p (note the square brackets). If you forget delete[] p , you will have a resource leak. You should never try to do one of the following, as they all lead to undefined behavior:

  • destroy a dynamic array via delete , free or any other means
  • destroy dynamic array several times
  • access to the dynamic array after its destruction.

If an exception is thrown when constructing the nth element, elements n-1 to 0 are destroyed in descending order, the base memory is freed, and the exception is thrown.

(Typically, for dynamic arrays, std::vector<Foo> over Foo* usually recommended, which greatly simplifies the writing of correct and reliable code.)

smart pointer links

A dynamic object managed by several std::shared_ptr<Foo> objects is destroyed during the destruction of the last std::shared_ptr<Foo> object participating in the sharing of this dynamic object.

(Normally, you prefer std::shared_ptr<Foo> over Foo* for shared objects, which greatly simplifies writing correct and reliable code.)

+75
Jun 19 2018-11-11T00:
source share

The object's destructor is called automatically when the object's lifetime expires and it is destroyed. Usually you should not call it manually.

We will use this object as an example:

 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";} }; 

In C ++, there are three (four in C ++ 11) different types of objects, and the type of object determines the lifespan of objects.

  • Static Storage Duration Objects
  • Auto Storage Duration Objects
  • Dynamic Storage Duration Objects
  • (In C ++ 11) Thread Storage Duration Objects

Static Storage Duration Objects

These are the simplest and equivalent to global variables. The lifespan of these objects (usually) is the length of the application. They are (usually) built before main is introduced and destroyed (in the reverse order of creation) after exiting the main.

 Test global; int main() { std::cout << "Main\n"; } > ./a.out Created 0x10fbb80b0 Main Destroyed 0x10fbb80b0 

Note 1: There are two more types of static storage duration object.

static class member variables.

It has the same meaning and purpose as global variables in terms of life expectancy.

static variables inside a function.

These are lazily created static objects of storage duration. They are created upon first use (in a thread-safe estate for C ++ 11). Like other static storage duration objects, they are destroyed when the application terminates.

Construction / demolition procedure

  • The build order within the compilation unit is well defined and matches the declaration.
  • The build order between compilation units is undefined.
  • The order of destruction is the exact inverse of the order of construction.

Auto Storage Duration Objects

This is the most common type of object and what you should use in 99% of cases.

These are the three main types of automatic variables:

  • local variables inside a function / block
  • member variables inside a class / array.
  • temporary variables.

Local variables

When exiting a function / block, all variables declared inside this function / block will be destroyed (in the reverse order of creation).

 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 

member variables

The lifespan of member variables is tied to the object that owns it. When the life of the owners ends, all its members also end. Therefore, you need to look at the lifetime of the owner, who obeys the same rules.

Note: members are always destroyed before the owner in the reverse order of creation.

  • Thus, for class members, they are created in the order of declaration.
    and destroyed in the reverse order
  • Thus, for array members, they are created in the order 0 → top and destroyed in the reverse order top → 0

temporary variables

These are objects created as a result of an expression but not assigned to a variable. Temporary variables are destroyed just like other automatic variables. The fact is that the end of their scope is the end of the operator in which they are created (this is usually ';').

 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 ';' 

Note. There are situations when the service life can be increased.
But this does not apply to this simple discussion. By the time you understand that this document will be second for you, and before it prolongs the life of a temporary, you do not want to do it.

Dynamic Storage Duration Objects

These objects have a dynamic lifespan and are created using new and destroyed when delete called.

 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 

For developers who come from languages ​​collected in garbage languages, this may seem strange (managing the lifespan of your object). But the problem is not as bad as it seems. In C ++, it is unusual to use dynamically allocated objects directly. We have management facilities to control their lifespan.

Closest to most of the other compiled GC collections is std::shared_ptr . This will keep track of the number of users of the dynamically created object, and when they all leave, you will call delete automatically (I think this is the best version of a regular Java object).

 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 

Thread Storage Duration Objects

They are new to the language. They are very similar to objects of duration of static storage. But instead of living in the same life as the application, they live as long as the thread of execution with which they are associated.

+34
Jul 29 '12 at 19:58
source share



All Articles