开发者

Will a base destructor always be called when a derived object is deleted?

开发者 https://www.devze.com 2023-01-23 20:50 出处:网络
say I have the following: class Foo { publi开发者_如何转开发c: void destroy(); ~Foo() { destroy(); }

say I have the following:

class Foo {
publi开发者_如何转开发c:
void destroy();
~Foo()
{
   destroy();
}
};

class Bar : public Foo {
public:
~Bar() {}
};

If I have a Bar object, will Foo's destructor be called when the Bar objected is deleted?

Thanks


Yes.

But the above code is dangerous; if you don't declare ~Foo() as virtual, then ~Bar() won't be called if you invoke delete on a Bar object via a Foo *.


Yes this will work if you hold a Bar reference. However this is a very fragile solution because it does not work the other way around (holding a Foo& or Foo* which points to a Bar). In order for this to work Foos destructor must be virtual. Otherwise it will do non-virtual dispatch and only call the destructor on Foo.

class Foo { 
public: 
void destroy(); 
virtual ~Foo() 
{ 
   destroy(); 
} 
}; 

class Bar : public Foo { 
public: 
virtual ~Bar() {} 
}; 

In the general case where inheritance is expected the types involved should be marked with a virtual destructor. Like most rules though this is not true 100% of the time. But rather than get into all of the cases here I encourage you to read question #2 in the following article (thanks for the link Steve)

  • http://www.gotw.ca/publications/mill18.htm


You make the base class's destructor virtual if the derived class could possibly have anything important going on. In fact, you should make it virtual if there's pretty much any chance someone will derive from your class.

  • http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7

In any case, if you implement a destructor, you should also implement the Big-3:

  • http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.10

Here's some code that should make the behavior of a virtual destructor obvious:

#include <iostream>

class Base1
{
public:
  ~Base1() // Uh-oh, not virtual...
  {
    std::cout << "~Base1()\n";
  }
};

class Derived1 : public Base1
{
public:
  ~Derived1()
  {
    std::cout << "~Derived1()\n";
  }
};

class Base2
{
public:
  virtual ~Base2() // Good, it's virtual...
  {
    std::cout << "~Base2()\n";
  }
};

class Derived2 : public Base2
{
public:
  ~Derived2()
  {
    std::cout << "~Derived2()\n";
  }
};

int main()
{
  std::cout << "Simple Base1: ";
  Base1* b1 = new Base1();
  delete b1;

  std::cout << "Simple Base2: ";
  Base2* b2 = new Base2();
  delete b2;

  std::cout << "Simple Derived1: ";
  Derived1* d1 = new Derived1();
  delete d1;

  std::cout << "Simple Derived2: ";
  Derived2* d2 = new Derived2();
  delete d2;

  std::cout << "Polymorphic Derived1: ";
  b1 = new Derived1();
  delete b1;

  std::cout << "Polymorphic Derived2: ";
  b2 = new Derived2();
  delete b2;

  return 0;
}

Simple Base1: ~Base1()
Simple Base2: ~Base2()
Simple Derived1: ~Derived1()
~Base1()
Simple Derived2: ~Derived2()
~Base2()
Polymorphic Derived1: ~Base1() Note - I read on another answer that this behavior is undefined, but that's just as bad, if not worse. Basically, make sure your base class has a virtual destructor
Polymorphic Derived2: ~Derived2()
~Base2()


I'm going to be a bit pedantic here and say "Yes, but there are cases when the answer would be No". For example:

class Foo {
public:
  void destroy();
  ~Foo()
  {
    destroy();
  }
};

class Bar : public Foo {
public:
  ~Bar() { exit (0); } // code never returns from exit call so base class
                       // destructor never executed!
};

Throwing an exception may also interrupt the normal destructor sequence. Also, any undefined behaviour in a destructor would eliminate the guarentee that base class desctructors are called (because anything could happen).

But generally, the base class destructor is called after the derived class destructor has completed.


You need to declare the destructor 'virtual' in the base class to get that behavior.

0

精彩评论

暂无评论...
验证码 换一张
取 消