From the C++17 standard (draft here), [expr.new]:
If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function, and the constructor. If the new-expression creates an array of objects of class type, the destructor is potentially invoked.
Why would new[] invoke a destructor? It's new, after all. It isn't delete.
If construction of any object in the buffer throws an exception, the previously constructed objects must be destructed. That requires an available destructor.
You have not considered the word "potentially" in the quote you have mentioned from the standard.
It means that there is a possibility that invocation of the destructor can happen. And it will happen if construction of any object in the array throws an exception.
Combined with the following quote from [class.dtor]/12.4 which mentions [expr.new], this becomes clear.
In each case, the context of the invocation is the context of the construction of the object. A destructor is also invoked implicitly through use of a delete-expression for a constructed object allocated by a new-expression; the context of the invocation is the delete-expression. [ Note: An array of class type contains several subobjects for each of which the destructor is invoked. — end note ] A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in [expr.new], [class.base.init], and [except.throw]. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.
In action:
#include <iostream>
int counter;
class Destruct
{
public:
Destruct()
{
if (counter++ > 5)
throw counter;
}
~Destruct()
{
std::cout << "Dtor called\n";
}
};
int main()
{
try
{
new Destruct[10];
}
catch (...){}
}
You'll see output something like:
Dtor called
Dtor called
Dtor called
Dtor called
Dtor called
Dtor called
Related
Does a destructor gets called before an object is destroyed or after an object is destroyed?
I think that it is called before an object is destroyed because after an object is destroyed we cant access that memory to free any resource in there but if I am wrong then please correct me so I can understand it well
#include <iostream>
#include <cassert>
#include <cstddef>
class Check
{
public:
int neu{};
Check() = default;
Check(int n)
{
neu = n;
}
void print()
{
std::cout << neu << std::endl;
}
~Check()
{
std::cout << "It has been destroyed "<<neu <<std::endl;
}
};
int main()
{
Check let,see(30);
let.print();
return 0;
// does destructor gets called here
} // or does destructor gets called herecode here
Does a destructor gets called before an object is destroyed or after an object is destroyed?
The lifetime of the object has ended when the destructor is called. The object is destroyed by the destructor.
// does destructor gets called here
} // or does destructor gets called herecode here
There's really no practical difference.
The automatic objects are alive within the block and they aren't alive outside the block. The objects are destroyed when the block is exited. As such, it is reasonable to say that they are destroyed at the point where the closing brace is.
From ISO standard (Special member functions - Destructors):
Once a destructor is invoked for an object, the object no longer
exists; the behavior is undefined if the destructor is invoked for an
object whose lifetime has ended.
Destructor call is essentially description of actions need to consider object destroyed. Destructor can be called explicitly, it results in end of life for that object. Explicit calls are rare, usually done for objects created by placement new.
Given the example provided, the question is actually about order of destructor calls. The destructors for sub-objects ( base class-type included, if any present) are called after. So you can safely access them until exit from destructor. Order of call is based on declaration order, just like order of initialization but inverted. Subobject of base-class type is considered the first for initialization and the last for destruction.
This question already has answers here:
Automatic destruction of object even after calling destructor explicitly
(5 answers)
Closed 3 years ago.
This code:
#include <iostream>
class Base { };
class Derived : public Base
{
public:
~Derived()
{
std::cout<< "Derived dtor" << std::endl;
}
};
int main()
{
Derived objD;
objD.~Derived();
return 0;
}
prints:
Derived dtor // destructor called
Derived dtor // printed by 'return 0'
I don't have idea from where comes the second line.
With this main:
int main()
{
Derived objD;
return 0;
}
It prints only one line.
What you have here is undefined behavior as per the C++ standard:
As per [class.dtor]/16
Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (6.8). [ Example: If the destructor for an automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined. —end example ]
You explicitly call the destructor for the automatic object objD with this statement:
objD.~Derived();
And the implicit destruction of the object is invoked at the end of its scope the closing }.
You create an object on the stack. This object has automatic lifetime managed by its surrounding scope. If you manually invoke the destructor, you do what the compiler does for you, and you end up with undefined behavior.
you call the destructor, c++ have an automated call for destructor after the main function, or after the object is not in used and then the destructor call. you do double "free"
Object destructors are always called when the object goes out of scope. That's a fundamental part of how C++ is designed, enabling memory-safe exceptions, RAII, etc. Manually calling the destructor first has no bearing on that, so if you call it yourself it will (most likely, this is UB) run twice as you can see.
Calling a destructor manually is almost always incorrect and will result in Undefined Behaviour. The one case where it's allowed is when you've created an object in separately-allocated memory via "placement new".
Let's consider that during the execution of the constructor of a class S, it appears that S could be constructed using another constructor. One solution could be to make a placement new at this to reuse the storage:
struct S{
unsigned int j; //no const neither reference non static members
S(unsigned int i){/*...*/}
S(int i){
if (i>=0) {
new (this) S(static_cast<unsigned int>(i));
return;}
/*...*/
}
};
int i=10;
S x{i};//is it UB?
Storage reuse is defined in [basic.life]. I don't know how to read this section when the storage is (re)used during constructor execution.
The standard is completely underspecified in this case, and I cannot find a relevant CWG issue.
In itself, your placement new is not UB. After all, you have storage without an object, so you can directly construct an object in it. As you correctly said, the lifetime of the first object hasn't started yet.
But now the problem is: What happens to the original object? Because normally, a constructor is only called on storage without an object and the end of constructor marks the start of the lifetime of the object. But now there is already another object. Is the new object destroyed? Does it have no effect?
The standard is missing a paragraph in [class.cdtor] that says what should happen if a new object is created in the storage of an object under construction and destruction.
You can even construct even weirder code:
struct X {
X *object;
int var;
X() : object(new (this) X(4)), var(5) {} // ?!?
X(int x) : var(x) {}
} x;
is it UB?
No it is not. [basic.life]/5 says:
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Emphasis on the part relevant to your class which has a trivial destructor. About the specific new (this) T; form, I found no exception to this rule in [class.cdtor] nor [class.dtor].
The following program :
#include <iostream>
using namespace std;
class Test
{
public:
Test() { cout << "Constructor is executed\n"; }
~Test() { cout << "Destructor is executed\n"; }
};
int main()
{
Test(); // Explicit call to constructor
Test t; // local object
t.~Test(); // Explicit call to destructor
return 0;
}
prints the following output:
Constructor is executed
Destructor is executed
Constructor is executed
Destructor is executed
Destructor is executed
My question is even after explicitly calling destructor in main(), why does the compiler call the destructor implicitly before exiting main()?
As a side question, apart from use in delete operator is there any other use of the strategy of calling destructor explicitly?
You've introduced undefined behavior.
Per the standard:
§ 12.4 Destructors
(11) A destructor is invoked implicitly
(11.3) — for a constructed object with automatic storage duration (3.7.3) when the block in which an object is
created exits (6.7),
and
15 Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the
destructor is invoked for an object whose lifetime has ended (3.8). [ Example: if the destructor for an
automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily
invoke implicit destruction of the object, the behavior is undefined. —end example ]
You explicitly call the destructor or by calling t.~Test(), it is then implicitly invoked when the object leaves scope. This is undefined.
The standard provides this note as well:
14 [ Note: explicit calls of destructors are rarely needed. One use of such calls is for objects placed at specific
addresses using a placement new-expression. Such use of explicit placement and destruction of objects can
be necessary to cope with dedicated hardware resources and for writing memory management facilities.
This is not an "explicit call to constructor":
Test(); // Explicit call to constructor
It constructs a temporary object, which implicitly calls the constructor, then the temporary immediately goes out of scope, which implicitly calls the destructor. You can't explicitly call a constructor, it gets called implicitly when you construct an object.
My question is even after explicitly calling destructor in main(), why does the compiler call the destructor implicitly before exiting main()?
Because the compiler always destroys local variables. Just because you did something dumb (manually destroyed an object that gets destroyed automatically) doesn't change that.
As a side question, apart from use in delete operator is there any other use of the strategy of calling destructor explicitly?
It's used when managing the lifetime of objects in raw memory, which is done by containers like std::vector and other utilities like std::optional.
My question is even after explicitly calling destructor in main(), why does the compiler call the destructor implicitly before exiting main()?
The destructor will be called when the object gets out of scope, regardless of whether you call the destructor explicitly or not. Don't explicitly call the destructor for objects with automatic storage duration.
As a side question, apart from use in delete operator is there any other use of the strategy of calling destructor explicitly?
Yes. When you initialize an object using the placement new expression, you need to call the destructor explicitly. Sample code from the above site:
char* ptr = new char[sizeof(T)]; // allocate memory
T* tptr = new(ptr) T; // construct in allocated storage ("place")
tptr->~T(); // destruct
delete[] ptr; // deallocate memory
the scope of t is the main function. Its created on the stack and will be destroyed at the end of the function.
That's how its supposed to work and when you call the destructor on it early, you don't change that.
You don't need to call the destructor and in this case doing so leads to it being called twice.
if you'd used
Test* t = new Test();
the destructor would not have been automatically called at the end of main.
The compiler does not care if you called the destructor explicitly or not. The local object t gets out of scope, that's why it gets destroyed and the destructor is called.
It is no good practice to call destructors explicitly. Instead you should write a method like cleanup() that can be called explicitly as well as from within the destructor. If you want to avoid that cleanup can be called twice, add something like this to you class:
private:
bool cleanupPerformed;
void cleanup()
{
if( !cleanupPerformed )
{
// ... do cleanup work here ...
cleanupPerformed = true;
}
}
This is a spinoff from invoking the copy constructor within the constructor.
I believe that an object is fully formed and can be expected to behave as such by the end of the initialiser list (edit: I was wrong about this though!). Specifically, member functions and accessing local state from within the constructor itself will behave exactly as they would from any other member function.
This seems to be a slightly contentious point of view though, the alternative is that only once the constructor has returned normally is the object fully formed.
The following is a quick & dirty test case for this which shows all the member fields that are mentioned in the initialiser list being initialised and those that aren't getting default constructed.
#include <cstdio>
struct noise
{
noise() { printf("noise default constructed\n"); }
noise(int x) { printf("noise integer constructed %u\n", x); }
~noise() { printf("noise dtor\n"); }
};
struct invoke : public noise
{
noise init;
noise body;
invoke() : noise(3), init(4)
{
body = noise(5);
throw *this; // try to use the object before returning normally
}
~invoke() { printf("invoke dtor\n"); }
};
int main()
{
try
{
invoke i;
}
catch (...)
{
}
}
This prints, on my machine at least,
noise integer constructed 3
noise integer constructed 4
noise default constructed
noise integer constructed 5
noise dtor
noise dtor
noise dtor
noise dtor
invoke dtor
noise dtor
noise dtor
noise dtor
As always, it's difficult to distinguish works-as-specified from works-as-my-compiler-implemented! Is this actually UB?
Is an object fully constructed at the end of the initialiser list?
No it is not. The object this is fully constructed at the end of the execution of the constructor.
However, all the members are constructed by the end of the initializer list.
The difference is subtle but it is important as it relates to the execution of the destructors. Every constructed member and base class is destructed if the this object throws an exception during the execution of the constructor. The destructor of the this object will only execute once it is fully constructed.
From the cppreference:
For any object of class or aggregate types if it, or any of its subobjects, is initialized by anything other than the trivial default constructor, lifetime begins when initialization ends.
For any object of class types whose destructor is not trivial, lifetime ends when the execution of the destructor begins.
Your example is well-defined behavior, but only just so.
To be clear, the "invoke dtor" line we're seeing is from the destruction of your exception, not the top-level i object.
Each member of the class is initialized by the time the constructor body starts, though the object itself is not initialized until the constructor body completes. This is why ~invoke is not called on the top-level invoke object.
The throw *this expression copy-initializes an invoke object from *this, which is allowed. (The standard explicitly states that "Member functions [...] can be called during construction or destruction".) Your copy-initialization is the default, which just copy-initializes all the members - which have all been initialized.
Then because your constructor body is exiting via exception, all the initialized members are destructed, the exception is propagated, caught, and then disposed of, calling ~invoke, and in turn destroying those members as well.