1

During my studing exeption's mechanism I found that there are calls of destructors for fields of the object while stack's unwinding. Let me explain explicitly:

class X
{
  File_ptr aa;
  Lock_ptr bb;
public:
 X(const char* x,const char* y):aa(x),bb(y){}
//.......
}

So,now if Lock_ptr's constructor throws an exeption, object aa will be destroyed; The question is "why"? I always thought that fileds of the object are not usual automatic (lochal) objects.They are created before constructor initializes them.So they can't be destryed after they're out of scope of constructor (Otherwise they would be destroyed as soon as constructor's finished its work)

  • 1
    By the time `bb`'s constructor is called, `aa` has been constructed. Hence, if an exception is thrown, its destructor is called. This is the basis of RAII. I really have no idea what exactly you're asking in the last paragraph. They are both automatic variables that are local to the class. – chris Jun 27 '14 at 17:43
  • .The question is "Why the field of the class are considered as automatic objects inside it's constructor ?" – Oleksii Hamov Jun 27 '14 at 17:46
  • What do you mean inside its constructor? If they were treated as local to the constructor, they would be destroyed before the object could be used. – chris Jun 27 '14 at 17:48
  • @chris: I avoid using the terms "automatic" and "local" with member objects, because the C++ spec says that they're dynamic if the parent object is dynamic, which makes the terminology a little counterintuitive... – Mooing Duck Jun 27 '14 at 17:52
  • @MooingDuck, Good point. They exhibit semantics like objects with automatic storage duration in relation to their containing class's lifetime. Any better? – chris Jun 27 '14 at 17:55
  • Let's consider it step by step: Lock_ptr's constructor thrown an exeptions and controlling stream back's to X's constructor. Then an exeptions can't be processed inside X's constructor (somehow there is a destruction of aa. How compiler can know that there is a need to call a destructor?aa is not automatic var. to a construtor. It means that data and constructor are separate and compiler can't know that it's necessary to call aa's destructor) and controlling stream backs to Main() (for example). – Oleksii Hamov Jun 27 '14 at 18:00

3 Answers3

5

Subobjects (including non-static data members) have the same storage duration as the complete objects to which they belong. This is not the same as saying that they are automatic. An automatic object is destroyed at the end of a block. A subobject is destroyed whenever its complete object is destroyed. For example, if the complete object is created with new and destroyed with delete (i.e. has dynamic storage duration), then the subobject is also created in the call to new, and destroyed in the call to delete. On the other hand, a subobject of an automatic object is also automatic.

If the constructor for X::bb throws an exception, then it means the complete object of type X cannot be constructed. All subobjects that have already been constructed, such as X::aa, must be destroyed, because a subobject, having the same storage duration as its complete object, cannot survive without the complete object.

On the other hand, if construction of the entire X object completes successfully, X::aa and other subobjects won't be destroyed until (shortly after) the complete X object is destroyed.

The construction and destruction rules for C++ are intended to guarantee that, as long as a program terminates normally, every object that is created is also destroyed exactly once. This is essential for the RAII idiom. In this example, if X::aa acquires resources when it is constructed, the language must ensure that those resources will be released. If X::aa's destructor is not called when construction of the X fails, then when should it be called?

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • +1 Supporting this, [see it live](http://ideone.com/GZgjWJ). In the linked demo, note that at the time the exception is thrown, only one of the three members of the Owner class are fully constructed, and it is the only one with its destructor invoked. Also note the order of the *destructors* of the array, and how *neither* constructor nor destructor of elements past the one that threw an exception is fired (because they technically don't "exist" yet). Nice answer, btw. – WhozCraig Jun 27 '14 at 18:11
  • @Brian,how compiler know that there is actually need to call destructors for all full-constucted objects ? What really calls destructors ? – Oleksii Hamov Jun 28 '14 at 13:26
  • 1
    @GamovCoder The compiler generates some extra code as part of the exception handling mechanism that destroys subobjects that have already been constructed. This code gets executed somehow as part of the implementation of the exception handling mechanism whenever an exception propagates out of a constructor. – Brian Bi Jun 28 '14 at 13:38
3

X has a "real" constructor that constructs aa and bb, and then after that the constructor body of X is called. This is why the initializer list is before X's constructor "body". Inside of this "real" constructor, the bases and then members are created as if they were on the stack in the order they're declared in the class, including for stack unwinding. I even have a picture: enter image description here

Destructors work on a vaguely similar principle, except in reverse, but can be even more complicated if the destructor is virtual. If the destructor is virtual, then there's three parts. There's a "stub" destructor that gets called when someone calls the destructor, which dispatches to the most-derived type's "real" destructor. The "real" destructor calls your destructor "body", and then destructs the members in the reverse order that they're declared in the class, and then it destructs the base classes (still in reverse order, same as a stack).

*QuestionC correctly observes that static members are completely independent of everything I wrote here.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Thanks, good picture :) I got one more question: Call of destructors for already constructed objects,what does guarantee it? Concrete implamintation of C++ or It somehow depends on scope ? – Oleksii Hamov Jun 27 '14 at 19:03
  • The "Real" constructor of `X` will destroy the subobjects and base types if an exception occurs, regardless of scope. The C++ compiler adds this when you compile. – Mooing Duck Jun 27 '14 at 19:14
  • ,does it mean that constructor calls destructors? – Oleksii Hamov Jun 27 '14 at 19:34
  • 1
    @GamovCoder: If an exception is thrown, yes – Mooing Duck Jun 27 '14 at 19:38
  • ,I found some interesting thing.If there is no handler for exeption then, there are not calls for destructors at all even if exeption is thrown... Object still exists not being totally constructed... And you still might get its full-constructed fields. It's destroyed when it get out of scope. Seems like stack's unwinding mechanism works somehow another way – Oleksii Hamov Jun 27 '14 at 20:25
  • @GamovCoder: § 15.3\9 "If no matching handler is found, the function std::terminate() is called; whether or not the stack is unwound before this call to std::terminate() is implementation-defined" If you have no handler, it's supposed to simply "crash". Can you show any code that does otherwise? – Mooing Duck Jun 27 '14 at 20:32
  • ,you're right, I just used lochal debuger,and I skipped the unhandled exeption... :) Thanks again! – Oleksii Hamov Jun 27 '14 at 20:43
0

Member objects (unless they are static) are constructed when the class constructor is called. The behavior you are seeing is normal and correct.

Member objects are not destroyed when the constructor is finished. They are destroyed when the class destructor is called (in reverse order of construction).

QuestionC
  • 10,006
  • 4
  • 26
  • 44