The article Are destructors overloadable? talks about overloading the destructor.
This raised a question: Can a destructor have parameters?
I've never used or seen a destructor with parameters. I could not come up with an example of a reason to use parameters to the destructor.
Section §12.4 of C++0x draft n3290 has this to say about destructors:
Destructors
A special declarator syntax using an optional function-specifier (7.1.2) followed by ˜ followed by the destructor’s class name followed by an empty parameter list is used to declare the destructor in a class definition.
(emphasis added)
So no, destructors do not take parameters. (The 2003 standard has the exact wording of the above paragraph.)
No, is the simple answer. This would make automatic resource management a significant bitch, because you'd have to worry about what parameters the destructor took and where the hell you were going to get them from. What about in the case of exception- how would the compiler know what to pass your destructor?
No. You hardly ever call them directly anyway, so what would be the use.
The destructor is supposed to destroy the object, nothing more.
Related
I am learning C++ using the books listed here. Now I came across the following statement from C++ Primer:
When we allocate a block of memory, we often plan to construct objects in that
memory as needed. In this case, we’d like to decouple memory allocation from object
construction.
Combining initialization with allocation is usually what we want when we
allocate a single object. In that case, we almost certainly know the value the object
should have.
(emphasis mine)
The important thing to note here is that C++ primer seems to suggest that construction is the same as initialization and that they are different from allocation which makes sense to me.
Note that I've just quoted selected parts from the complete paragraph to keep the discussion concise and get my point across. You can read the complete para if you want here.
Now, I came across the following statement from class.dtor:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
(emphasis mine)
Now does the standard specifies exactly when(at what point) the constructor execution begins?
To give you some more context consider the example:
class A {
public:
A(int)
{
}
};
class B : public A {
int j;
public:
int f()
{
return 4;
}
//------v-----------------> #2
B() : A(f()),
//------------^-----------> #3
j(f())
//------------^-----------> #4
{ //<---------------#5
}
};
int main()
{
B b; #1
return 0;
}
My questions are:
At what point does the derived class' constructor B::B() start executing according to the standard? Note that I know that A(f()) is undefined behavior. Does B::B() starts executing at point #1, #2, #3, #4 or #5. In other words, does the standard specifies exactly when(at what point) the constructor execution begins?
Is construction and initialization the same in this given example. I mean I understand that in the member initializer list where we have j(f()), we're initializing data member j but does this initialization also implies that the construction B::B() has begun executing at point #4?
I read in a recent SO post that execution of derived ctor begins at point #4 and so that post also seem to suggest that Initialisation and construction is the same.
I read many posts before asking this question but I wasn't able to come up with an answer that is right according to the C++ standard.
I also read this which seems to suggest that allocation, initialization and construction are all different:
Allocation
This is the step where memory is allocated for the object.
Initialization
This is the step where the language related object properties are "set". The vTable and any other "language implementation" related operations are done.
Construction
Now that an object is allocated and initialized, the constructor is being executed. Whether the default constructor is used or not depends on how the object was created.
As you can see above, the user claims that all of the mentioned terms are different as opposed to what is suggested by C++ primer. So which claim is correct here according to the standard, C++ Primer(which says that construction and Initialisation is same) or the above quoted quoted answer(what says that construction and Initialisation are different).
Initialization and construction are somewhat similar, but not the same.
For objects of class types, initialization is done by calling a constructor (there are exceptions, e.g. aggregate initialization doesn't use a constructor; and value-initialization zeros the members in before calling a constructor).
Non-class types don't have constructors, so they are initialized without them.
Your C++ Primer quote uses "initialization" and "construction" to refer to the same thing. It would be more correct to call it "initialization" to not limit yourself to class types, and to include other parts of initialization other than a constructor call.
does the standard specifies exactly when(at what point) the constructor execution begins?
Yes. B b; is default-initialization, which, for classes, calls the default constructor and does nothing else.
So the default-constructor B() is the first thing that's executed here.
As described here, B() calls A() after evaluating its argument f(), then initializes j after evaluating its initializer f(). Finally, it executes its own body, which is empty in your case.
Since the body is executed last, it's a common misunderstanding to think that B() itself is executed after A() and/or after initializing j.
I read in a recent SO post that execution of derived ctor begins at point #4
You should also read my comments to that post, challenging this statement.
There is no distinction between point 1 and 2 in your example. It's the same point. An object's constructor gets invoked when the object gets instantiated, when it comes into existence. Whether it's point 1 or point 2, that's immaterial.
What you are separating out here is the allocation of the underlying memory for the object (point 1) and when the new object's constructor begins executing (point 2).
But that's a distinction without a difference. These are not two discrete events in C++ that somehow can be carried out as discrete, separate steps. They are indivisible, they are one and the same. You cannot allocate memory for a C++ object but somehow avoid constructing it. Similarly you cannot construct some object without allocating memory for it first. It's the same thing.
Now, you do have other distractions that can happen here, like employing the services of the placement-new operator (and manually invoking the object's destructor at some point later down the road). That seems to suggest that allocation is something that's separate, but its really not. The invocation of the placement new operator is, effectively, implicitly allocating the memory for the new object from the pointer you hand over to the placement new operator, then this is immediately followed by the object's construction. That's the way to think about it. So allocation+construction is still an indivisible step.
Another important point to understand is that the first thing that every object's constructor does is call the constructors of all objects that it's derived from. So, from a practical aspect, the very first thing that actually happens in the shown code is that A's constructor gets called first, to construct it, then, once its construction is finished, then B's "real" construction takes place.
B() : A(f()),
That's exactly what this reads. B's constructor starts executing, and its first order of business is to call A's constructor. B does nothing else, until A's constructor finishes. So, technically, B's constructor starts executing first, then A's constructor. But, B's constructor does nothing until A's constructor handles its business.
The first thing that B's constructor does is call A's constructor. This is specified in the C++ standard.
B() : j(f()), A(f())
If you try to do this your compiler will yell at you.
So, to summarize: when you instantiate an object, any object, the very first thing that happens is its constructor gets called. That's it. End of story. Every possible variation here (placement new, PODs with no constructors, the additional song-and-dance routine with virtual dispatch) is all downstream of this, and can be defined as special, and specific, implementations of constructors.
This question already has answers here:
Will an 'empty' constructor or destructor do the same thing as the generated one?
(7 answers)
Closed 6 years ago.
I'm new to C++, and one of the concepts I'm working on understanding is destructors. Out of curiosity, can an unnecessary (e.g., when a class has no dynamically allocated memory, resources, or anything requiring a user-defined destructor) and empty destructor cause any unforeseen problems?
Edit: I know that part of this has been answered in Will an 'empty' constructor or destructor do the same thing as the generated one? but I wanted to broaden it to ask more about generalized negative consequences such as crashes or making an application slower. There is some overlap, but it is a slightly different question.
The question depends on several parameters. Emptiness isn't the only thing that has effect on the result. E.g., if you don't define virtual destructor (empty or not), you'll get problematic behavior when inheriting from the class. On the other hand, if you define an empty destructor in private or protected section, it will prevent creating instances of the class on stack.
There is also an interesting aspect (which do not seem to be talked about in the linked duplicate) of a triviality of destructor. Compiler-generated (or defaulted) destructors are considered trivial destructors, and having a trivial destructor is a pre-requisite of your class being a POD-type. The user-defined destructor, even if empty, prevents your class from being a POD-type.
And having a POD-type is sometimes very important. For example, POD-types can be memcpyed or entity-serialized.
No, all members of the instance are still destroyed after your destructor ran. The only thing a destructor must not do is throwing an exception, otherwise it may do anything a "proper" method can do, i.e. also doing nothing at all is fine. Not closing handles when you should have is another question.
No. In fact, if you don't declare and write a destructor for a class or struct, the compiler will do it for you - and it will be empty.
"Empty destructor" is a bit of a misnomer. Whether or not your destructor has a body, the compiler will still generate code to call the destructor of every non-static member variable and base class in reverse order of declaration. You only need a body if you wish to do something before those other destructors get called.
I am reading textbook "C++ Primer Plus, Prata"
A paragraph in chapter 10 catches my eye and confuses me.
Ch.10 Objects and classes:
It says
If you don't provide one, the compiler implicitly declares a default constructor and,...
I thought it should be
If you don't provide destructor, the compiler implicitly declares a default destructor and,...
Is the paragraph correct?
How should I explain that correctly?
Thank you
The "one" part is correct. That's just a nuance of English grammar where you can refer to something in a dependent clause that comes later in the sentence. Think of it like a forward declaration! The "default constructor" part is actually a typo: it should be "default destructor", like you originally thought.
It should say this:
Because a destructor is called automatically when a class object expires, there ought to be a destructor. If you don't provide one [a destructor], the compiler implicitly declares a default destructor and, if it detects code that leads to the destruction of an object, it provides a definition for the destructor.
Here, "one" refers to "a destructor," which comes later in the sentence. Another key to understanding the sentence is keeping in mind the distinction between declaring a function and defining a function. The compiler always declares an implicit destructor if you don't provide one, but it only defines it if it needs it (that is, if that destructor is going to be called).
What makes it all the more confusing (and probably what led to the typo) is that all of this is equally true for constructors.
Let's see if we can improve on the paragraph:
Because the destructor will be called automatically when a class object goes out of scope, all classes must have a destructor. If you don't explicitly provide one, the compiler implicitly declares a default destructor. If the compiler detects code that leads to the destruction of an object, it also provides a default definition for the destructor. The same thing is true for constructors.
How can std::shared_ptr offer a noexcept operator=? Surely, if this shared_ptr is the last one, then it will have to destroy its contents, and it can't guarantee that the destructor of that object does not throw, or the custom deleter used originally does not throw.
Looks like a defect to me, though not one I can find in the active issues list (though #2104 is similar).
Per [C++11: 20.7.2.2.3/1], the assignment is defined to be equivalent to shared_ptr(r).swap(*this);
But per [C++11: 20.7.2.2.2], ~shared_ptr itself is not noexcept.
Unless I've misunderstood the way in which noexcept works, this must be an error.
Alternatively it could simply mean that the assignment operator is only usable when neither the underlying object type nor the deleter type throw on destruction, though even in such a scenario, the lack of any informative note in the standard wording makes me think that this is unlikely.
According to the isocpp forums, shared_ptr simply assumes that the deleter will not throw, and otherwise is UB. This would mean that the real defect is that shared_ptr's destructor is not marked as nothrow.
It's important to note that reset() (without parameters) and swap are declared nothrow as well.
Also if we take a look at boost::shared_ptr it provides the same declarations, except it also declares it's destructor as never throws which std::shared_ptr for some reason doesn't.
As far as I understand, what it means is not "I guarantee that ~T() will not throw", but "I prohibit ~T() to throw and hope you know what you are doing".
One of the golden rules in C++ is that the life-time of an instance begins when its constructor completes successfully and ends when its destructor begins.
From this rule we conclude that it is NOT a good idea to call virtual methods in a constructor as the possible derived instance is not valid which would lead to undefined behavior.
The Virtual Constructor Idiom as mentioned in C++ FAQ 20.8 seems to indicate the contrary.
My question is:
What is the exact definition in the standard for defining the life time of objects relative to calls from their constructors and destructors?
and furthermore is the So called "Virtual Constructor Idom" valid?
I think you're confusing two separate (if vaguely related) things.
It is well-known that one shouldn't call virtual functions from constructors (directly or indirectly), for reasons discussed here.
What the FAQ is talking about is calling a constructor from a virtual function. In some sense it is the opposite of #1. The idea is to choose the constructor (i.e. the class) based on the dynamic type of some existing object.
An object exists as the type of the class the constructor belongs to when the constructor starts, the object just may be in an inconsistent state (which is fine, as execution is currently inside one of the object's methods). That is, an object that is actually of type Derived that inherits from Base is treated as a Base in Base constructors; this in any Base::Base() is treated as a Base *, no matter the actual type of the object that this points to. Calling virtual methods is fine. In a constructor, the virtual methods for the constructor's class will be called, rather than for the actual type of the object.
Your first question is covered in "Base class on the initialisation list of a derived class' copy constructor". The short answer is it's covered by § 12.7 2-3 of C++03.
The virtual constructor idiom is completely valid.