destructor in C++ - c++

I had a constructor in my AB.h file:
class AB{
private: int i;
public:
AB:i(0){}//constructor
~AB:i(0){} // destructor
virtual void methodA(unsigned int value)=0;};
The compiler said that:
class AB has virtual functions but non-virtual destructor
AB.h: In destructor ‘AB::~AB()’:
AC.h: error: only constructors take base initializers
if I use the ~AB(); destructor, it said that i have virtual functions but i didn't have destructor, where did I misunderstand?
Thankyou

Using an initialization list such as
AB : a_member(4),another_member(5) {}
makes only sense (and is permitted) for constructors - in a destructor, you don't want to initialize things.
In addition to this obvious syntax error, the compiler warns because AB has a virtual method but doesn't declare it's destructor virtual as well. This is recommended because of the following:
AB* ab = new SomethingDerivedFromAB();
delete ab; // calls only AB's dtor and not SomethingDeriveFromAB's unless AB declares its dtor virtual

You're getting an error and an unrelated warning.
The error is because you're using an initializer for your destructor, which doesn't make sense and isn't valid syntax.
You want:
~AB() { } // destructor
The warning is because you haven't declared your destructor virtual. Classes with virtual methods should have virtual destructors:
virtual ~AB() { }

If you have virtual functions, that's an indicator that the class is meant to be subclassed.
If you don't declare the destructor as virtual, a person with a pointer to the base class could call the destructor which won't call the sub-class's destructor because it's not a virtual destructor. Such a condition would leave the memory / items managed by the subclass in limbo as they wouldn't be properly cleaned up.
The lesson to be learned is to always make the destructor virtual, even if you already provide an implementation.

You should use virtual ~AB { } or protected: ~AB { } for your destructor.
The member initialization list : var(whatever) is for constructing objects and doesn't make sense in a destructor.
The warning about virtualness is because in general if you intend your class to be used polymorphically, you want it to be able to be deleted polymorphically as well. Alternately make the destructor protected so you can't polymorphically destroy your objects from a parent pointer.

The destructor should be virtual as you are planing to inherit from this class (methodA is virtual). However, this is just a warning.
The error is that the destructor has no argument, and obviously no initializer.
virtual ~AB() {}

class AB{
private: int i;
public:
AB:i(0){}
virtual ~AB(){}
virtual void methodA( unsigned int value ) = 0 ; };

About the virtual destructor, see the Rule of Three.

You are missing parentheses and initializing in the destructor: O.o
class AB
{
private:
int i;
public:
AB():i(0){} // <-- parentheses here
virtual ~AB() {} // <-- parentheses here
virtual void methodA(unsigned int value)=0;
};

The compiler is warning you that, while you have a virtual method in your class, your destructor is not virtual. This can lead to problems, since there may be point in your program where only the base destructor will be called.

Related

Why a pure virtual destructor needs an implementation

I know the cases where pure virtual destructors are needed. I also know that If we don't provide an implementation for them it will give me a linker error. What I don't understand is why this should be the case in a code fragment as shown below:
int main()
{
Base * p = new Derived;
}
Here there is no delete, so no call to destructor and so no need for its implementation(assuming it is supposed to behave like other normal functions which are declared but not defined, linker complains only when we call them)...or am I missing something?
I need to understand why this should be a special case?
Edit: based on comments from BoBTFish
Here are my Base and Derived classes
class Base
{
public:
Base(){}
virtual ~Base() = 0;
};
class Derived : public Base
{
};
The compiler tries to build the virtual table given a virtual (pure or not) destructor, and it complains because it can't find the implementation.
virtual destructors differ from other virtual functions because they are called when the object is destroyed, regardless of whether it was implemented or not. This requires the compiler to add it to the vf table, even if it's not called explicitly, because the derived class destructor needs it.
Pedantically, the standard requires a pure virtual destructor to be implemented.
C++11 standard:
12.4 Destructors
Paragraph 9:
A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any
derived class are created in the program, the destructor shall be defined. If a class has a base class with a
virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.
Destructors differ from other virtual functions in this way, because they are special and automatically invoked in bases, with no possible, useful or meaningful way to prevent it.
[C++11: 12.4/9]: A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.
Bases are always destroyed, and to do this, a base destructor definition is required. Conversely, other overridden virtual functions are not invoked automatically at all. Hence the special-case requirement.
struct Base
{
virtual ~Base() = 0; // invoked no matter what
virtual void foo() = 0; // only invoked if `Base::foo()` is called
};
Base::~Base() {}
/* void Base::foo() {} */
struct Derived : Base
{
virtual void foo() { /* Base::foo(); */ }
};
int main()
{
std::unique_ptr<Base> ptr(new Derived());
}
One practical reason is that destructors come first in the list of virtual member functions in the vtable in practically all implementations. And implementations tend to define the vtable itself when it defines the first virtual member function. So, no destructor, no vtable. And the vtable is critical.

Override Destructor C++

From the C++ FAQ:
[11.4] Can I overload the destructor for my class?
No.
I realize this means you cannot change the return type, arguments' types nor the number of arguments. I may be splitting hairs on the syntax of the words, but is it possible to override the Parent's destructor?
class Child : public Parent {
public:
virtual Parent::~Parent() {
// New definition
}
};
And for that matter do it recursively?
class Grandchild : public Child {
public:
Child::Parent::~Parent() {
// An even newer definition
}
};
I've read this and a related post and it makes me think because destructors are not inherited, they cannot be overridden, but I've never seen it explicitly stated.
EDIT: I changed this to reflect the fact that I want to override the Parent's destructor, note Child and Grandchild overriding ~Parent().
The main reason I am doing this is to maintain Parent's interface while changing the way it is destroyed (the entire reason for the child class). I will have something else managing all Parent's created and will explicitly call their destructors at a later time of my choosing.
I may be splitting hairs on the syntax of the words
No, you are definitely not – these are two very different things.
but is it possible to override the destructor?
Yes, and in fact you must do this in many cases. In order for this to work for a polymorphic object, you need to declare the base class destructor as virtual, though:
Parent const& p = Child();
Will properly call p.~Child() at the end of scope because Parent::~Parent is virtual.
Yes, it is possible to override the destructor of a class. In fact, when you define a class hierarchy in which polymorphism is used, you must declare a virtual destructor in the base class.
Overrides of destructors work exactly the same way overrides of normal member functions work in that when you destroy an object by deleteing the object via a pointer to the base class, the destructor of the derived class is properly called. This is why you must have a virtual destructor in the base class for polymorphic hierarchies.
However, there is a difference between virtual destructors and virtual member methods which has nothing to do with the virtual nature of the destructor. That is, when executing code like this:
class A
{
public:
virtual void Foo() {}
virtual ~A() {};
};
class B : public A
{
public:
void Foo() {};
~B() {}
};
int main()
{
A* a = new B;
a->Foo(); // B::Foo() is called
delete a; // B is destroyed via B::~B()
}
...when you call a->Foo(), the method Foo() in B is called. Since B::Foo() doesn't explicitly call A::Foo(), A::Foo() isn't called.
However, when the object is destroyed via delete a;, first the destructor B::~B() is called, and then after that finishes but before control returns to the program, the base class destructor A::~A() is also called.
Of course this is obvious when you think about it, and again this has nothing to do with the virtual nature of the destructor, but it does behave differently than a normal virtual method call, so I thought I'd point it out.
Obligitory Standard Quotation:
[C++03] 12.4/6 : Destructors
After executing the body of the destructor and destroying any
automatic objects allocated within the body, a destructor for class X
calls the destructors for X’s direct members, the destructors for X’s
direct base classes and, if X is the type of the most derived class
(12.6.2), its destructor calls the destructors for X’s virtual base
classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual
overriding destructors in more derived classes. Bases and members are
destroyed in the reverse order of the completion of their
constructor (see 12.6.2). A return statement (6.6.3) in a destructor
might not directly return to the caller; before transferring control
to the caller, the destructors for the members and bases are called.
Destructors for elements of an array are called in reverse order of
their construction (see 12.6).
Yes: you can have virtual destructors, and the only reason is to override them in derived classes.
It looks like this:
class Parent {
public:
virtual ~Parent();
};
class Child : public Parent {
public:
virtual ~Child();
};
class Grandchild : public Child {
public:
~Grandchild(); // virtual is inherited here
};
Note that the destructor isn't overridden by name like ordinary functions, because the name is always that of the class whose instance you're destroying.
Note also that the parent class' destructors are always called too, so you don't need to duplicate their cleanup code: read up on member object and base-class sub-object construction and destruction order for the details.
Terminology
overriding a function means implementing a base-class virtual function in a derived class. You can't change the signature at all (except for using covariant return types). So, an override always has the same signature as an inherited virtual function.
overloading a function means implementing multiple functions with the same name (and in some sense the same scope). So, an overload always has a different signature to the others with the same name, doesn't relate directly to virtual dispatch, and isn't necessarily inherited.
Yes; you can, and should, make a destructor virtual, whenever you have a child class which may be destroyed using a reference to the base class. Static code analysis tools will even complain if you don't offer a virtual destructor.
Consider the following example:
class A
{
public:
A() { a = new int; }
virtual ~A() { delete a; }
private:
int *a;
};
class B final : public A
{
public:
B() { b = new int; }
~B() { delete b; }
private:
int *b;
};
int main()
{
A *a = new B();
delete a;
}
If A's destructor was not virtual, then delete a would only call A's destructor, and you would end up with a memory leak. But because it's virtual, both destructors will be called, in the order ~B() -> ~A().

Will the compiler-generated destructor of an abstract base class be virtual?

class Base
{
virtual void foo() = 0;
//~Base(); <-- No destructor!
};
Obviously, Base will be derived. So, does C++ says the compiler-generated destructor of Base must be virtual?
Thanks!
No, the destructor will not be virtual unless you mark it as such. The reason is simple - calls can be made virtually both via pointers and via references and how and whether you make calls virtually is unrelated to whether you create objects with new. If you don't create objects with new you don't have to delete them and so you don't need virtual destructors.
It does not. This is close to a proof that the destructor is not automatically made virtual:
#include <iostream>
struct BaseBase {
~BaseBase() {
std::cout << "~BaseBase\n";
}
};
struct Base : BaseBase
{
virtual void foo() = 0;
//~Base(); <-- No destructor!
};
struct Derived : Base {
void foo() { std::cout << "foo\n"; }
~Derived() {
std::cout << "~Derived\n";
}
};
int main() {
Base *p = new Derived();
delete p;
}
This program actually has undefined behavior, but I strongly suspect that on your implementation it does not print "~Derived". If Base had a virtual destructor, then it would not have undefined behavior, and it would print "~Derived".
Of course it doesn't actually prove anything about the standard. Any implementation you run it on might after all be non-conforming. But once you've tried it on a few, you'll get the idea that whatever the standard might say, you need to specify a virtual destructor.
No, the dtor is not guaranteed to be virtual.
When declaring classes specifically designed to be derived from, its good practice to explicitly declare a virtual dtor. It's typically an outright design flaw not to. In fact, I can't think of a case where its not a design flaw to omit the virtual dtor from the base class.
No. A class can have virtual members, can be derived and can even be allocated with new and deleted with delete without having a virtual destructor.
What is illegal (UB) to do is to destroy a derived instance with delete using a pointer to base if the destructor is not declared virtual.
Of course there are no reason at all for not declaring a virtual destructor if your class is meant to be derived.

Why should the destructor of base classes be virtual?

in C++: Why should the destructor of base classes be virtual?
The better question is when and why. Your question indicates that you think all base classes should have virtual destructors, which is not quite true.
It would make it impossible to apply the empty base class optimization, and could multiply the size of classes up to 16 times than what it would be without virtual on common platforms.
A virtual destructor is needed when you delete an object whose dynamic type is DerivedClass by a pointer that has type BaseClass*. The virtual makes the compiler associate information in the object making it able to execute the derived class destructor. Missing the virtual in such case causes undefined behavior.
If you don't need this, and your class is only used as a base class, it's best to make the destructor protected, thus preventing that users accidentally delete in the described way.
You want them to be virtual so that all subclass destructors are automatically called when the object is destroyed, even if it is destroyed through a pointer to the base class. In the following code:
class base {
public:
virtual ~base() { }
};
class derived : public base {
public:
~derived() { } // Inherits the virtual designation
};
int main(void)
{
base *b = new derived;
delete b;
}
The derived destructor will only be called if the base destructor is virtual.
As Magnus indicates, you don't have to do this if you aren't taking advantage of polymorphism. However, I try to develop the habit of declaring all my destructors virtual. It protects me against the case where I should have declared them virtual but forget to do so. As Johannes indicates, this habit can impose a small space and performance penalty when the virtual designation is not needed.
They dont have to be virtual unless you are using polymorphism. If you do use polymorphism they need to be virtual so that the destructors of inherited classes are guaranteed to be called, so inherited classes can do their clean up.
For situations like this:
class A
{
virtual ~A();
};
class B:A
{
~B();
};
A *a = new B(); //legal, since it's a downcast
delete a; //Unless the destructor is virtual, ~A() is called here instead of ~B().
It should be virtual to ensure that the destructor of the inherited classes are the ones actually getting called at runtime instead of the base class destructor being called.

How do virtual destructors work?

I am using gcc. I am aware how the virtual destructors solve the problem when we destroy a derived class object pointed by a base class pointer. I want to know how do they work?
class A
{
public:
A(){cout<<"A constructor"<<endl;}
~A(){cout<<"A destructor"<<endl;}
};
class B:public A
{
public:
B(){cout<<"B constructor"<<endl;}
~B(){cout<<"B destructor"<<endl;}
};
int main()
{
A * a = new B();
delete a;
getch();
return 0;
}
When I change A's destructor to a virtual function, the problem is solved. What is the inner working for this. Why do I make A's destructor virtual. I want to know what happens to the vtable of A and B?
Virtual destructor is just a virtual function, so it adheres to the same rules.
When you call delete a, a destructor is implicitly called. If the destructor is not virtual, you get called a->~A(), because it's called as every other non-virtual function.
However if the destructor is virtual, you get ~B() called, as expected: the destructor function is virtual, so what gets called is the destructor of derived class, not base class.
Edit:
Note that the destructor of the base class will be called implicitly after the destructor of the derived class finishes. This is a difference to the usual virtual functions.
The key thing you need to know is that not using a virtual destructor in the above code is undefined behavior and that's not what you want. Virtual destructors are like any other virtual functions - when you call delete the program will decide what destructor to call right in runtime and that solves your problem.