8

Let us assume I have two classes:

class Base{};

class Derived: public Base{};

none has d'tor, in this case if I declare about variables:

Base b;
Derived d;

my compiler will produce for me d'tors, my question is, the default d'tors of the b and d will be virtual or not?

kevinarpe
  • 20,319
  • 26
  • 127
  • 154
rookie
  • 7,723
  • 15
  • 49
  • 59
  • possible duplicate of [Virtual Default Destructors in C++](http://stackoverflow.com/questions/827196/virtual-default-destructors-in-c) –  Oct 11 '10 at 18:54

6 Answers6

8

my question is, the d'tors of the b and d will be virtual or not

No, they won't. If you want a virtual destructor, you will have to define your own, even if its implementation is exactly the same as that which would be supplied by the compiler:

class Base {
  public:
    virtual ~Base() {}
};
sbi
  • 219,715
  • 46
  • 258
  • 445
7

The destructors of Base and Derived will not be virtual. To make a virtual destructor you need to mark it up explicitly:

struct Base
{
    virtual ~Base() {}
};

Actually there's now only one reason to use virtual destructors. That is to shut up the gcc warning: "class 'Base' has virtual functions but non-virtual destructor". As long as you always store your allocated objects in a shared_ptr, then you really don't need a virtual destructor. Here's how:

#include <iostream>   // cout, endl
#include <memory>     // shared_ptr
#include <string>     // string

struct Base
{
   virtual std::string GetName() const = 0;
};

class Concrete : public Base
{
   std::string GetName() const
   {
      return "Concrete";
   }
};

int main()
{
   std::shared_ptr<Base> b(new Concrete);
   std::cout << b->GetName() << std::endl;
}

The shared_ptr will clean up correctly, without the need for a virtual destructor. Remember, you will need to use the shared_ptr though!

Good luck!

Daniel Lidström
  • 9,930
  • 1
  • 27
  • 35
  • 1
    @Daniel: Really? Will the shared_ptr clean up correctly? Could you please in this case demonstrate how that effect could be implemented? – Armen Tsirunyan Oct 10 '10 at 09:20
  • @Armen: The shared_ptr uses its own destructor to delete the Concrete instance. This is known as RAII within the C++ community. My advice is that you learn all you can about RAII. It will make your C++ coding so much easier when you use RAII in all situations. – Daniel Lidström Oct 10 '10 at 09:24
  • @Daniel: I know about RAII, and I also know that eventually the shared_ptr destructor may delete the stored px when pn reaches 0. But if px had static type pointer to Base and dynamic type pointer to Derived, then unless Base has a virtual destructor, this will result in undefined behavior. Correct me if I am wrong. – Armen Tsirunyan Oct 10 '10 at 09:27
  • 1
    @Daniel: Even with RAII, at some low level of your design, there will be classes (i.e. those that directly wrap the resources) that must do explicit cleanup upon destruction. – Oliver Charlesworth Oct 10 '10 at 09:29
  • 2
    @Armen: The shared_ptr knows the static type is Concrete. It knows this since I passed it in its constructor! Seems a bit like magic, but I can assure you it is by design and extremely nice. – Daniel Lidström Oct 10 '10 at 09:31
  • 2
    @Oli: When you apply RAII recursively, always using shared_ptr, the effect is that suddenly you don't really need a destructor at all! shared_ptr's can wrap any resource and it is even more convenient when you can specify the custom deleter using the new lambda syntax. – Daniel Lidström Oct 10 '10 at 09:32
  • @Daniel: Right, but whatever lives at the end of your "recursion" must explicitly do clear-up that might be more than just memory deallocation (imagine a database-connection class, or a file I/O class). – Oliver Charlesworth Oct 10 '10 at 09:35
  • @Daniel, @Oli: OK, I made a new topic on this issue, let's discuss there :) – Armen Tsirunyan Oct 10 '10 at 09:40
  • @Daniel: your example won't work because you use a `std::shared_ptr` which wants a `Base*` for its constructor, so it won't know that its internal object is in fact a `Concrete`. However this code will work: `std::shared_ptr b = std::shared_ptr(new Concrete)` – Tomaka17 Oct 10 '10 at 09:52
  • @Tomaka17: A pointer to Concrete is convertible to a pointer to Base. That's why it works. – Daniel Lidström Oct 10 '10 at 09:53
  • @Oli: A custom deleter can handle any type of clean-up. – Daniel Lidström Oct 10 '10 at 09:58
  • 7
    I think that it's poor advice to imply that polymorhphic base classes should have virtual destructors only to silence a warning. Not providing a virtual destructor for a class designed to be a common base class for an inheritance hierarchy forces the user to use `shared_ptr` or equivalent. Classes that don't attempt to manage how they are used are more versatile. Giving such a class a virtual destructor allows it to be used correctly in more situations at little extra cost. A user would typically get no warning about incorrect use in this case. Classes should be made easy to use correctly. – CB Bailey Oct 10 '10 at 10:15
  • 2
    @Charles: You are right. As I have no way to oversee the OP's work I can't know he follows the requirements. So my advice was not really appropriate for a beginner. If he was working with me though, I would lead him on to the path of design patterns. Any interface hierarchy would need the use of an abstract factory. However, that's a discussion I don't want to have here. – Daniel Lidström Oct 10 '10 at 10:25
  • @Daniel: I think you don't understand the problem. If you call the constructor of `std::shared_ptr` by giving it a `Base*` pointer (converted from a `Concrete*` pointer), the shared_ptr will call the destructor of Base, but the destructor of Concrete won't be called. This is because it is not possible for the shared_ptr to know that this is a Concrete object. – Tomaka17 Oct 10 '10 at 10:30
  • The best way to safely build a shared_ptr is to use `std::make_shared(...parameters for Concrete's constructor...)` – Tomaka17 Oct 10 '10 at 10:36
  • 2
    @Tomaka17: You are correct that `Base* pBase = new Concrete; shared_ptr shpBase(pBase);` is dangerous but that's part of Daniel's point. All he was saying is that `shared_ptr shpBase(new Concrete);` is not dangerous, which it isn't, because of `shared_ptr`'s constructor template. – CB Bailey Oct 10 '10 at 10:40
  • @Charles: I just checked and you're right. This used to be dangerous in boost, and I didn't know they changed it. – Tomaka17 Oct 10 '10 at 10:46
5

my question is, the d'tors of the b and d will be virtual or not

Short answer : Nopes!

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
3

They will NOT be virtual.However, if you declared(and defined) a virtual dtor in Base, then the derived's dtor would be automatically virtual. HTH.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
1

Just to add one more example to Daniel Lidström's answer

As long as you always store your allocated objects in a shared_ptr, then you really don't need a virtual destructor.

If one uses a shared_ptr like this:

    std::shared_ptr<Base> b(new Concrete);

Then the Concrete destructor and the Base destructor are called on destruction of the object.

If one uses a shared_ptr like this:

Base* pBase = new Concrete;
std::shared_ptr<Base> b(pBase);

Then only the Base destructor is called on destruction of the object.

This is an example

#include <iostream>   // cout, endl
#include <memory>     // shared_ptr
#include <string>     // string

struct Base
{
   virtual std::string GetName() const = 0;
   ~Base() { std::cout << "~Base\n"; } 
};

struct Concrete : public Base
{
   std::string GetName() const
   {
      return "Concrete";
   }
   ~Concrete() { std::cout << "~Concrete\n"; } 
};

int main()
{
  {
    std::cout << "test 1\n";
    std::shared_ptr<Base> b(new Concrete);
    std::cout << b->GetName() << std::endl;
  }

  {
    std::cout << "test 2\n";
    Base* pBase = new Concrete;
    std::shared_ptr<Base> b(pBase);
    std::cout << b->GetName() << std::endl;
  }

}
Community
  • 1
  • 1
1

How can they be virtual unless you explicitly make them as virtual

Ashish Yadav
  • 1,667
  • 6
  • 20
  • 26