我对大多数OOP理论都有很好的理解,但最让我困惑的是虚拟析构函数。

我以为析构函数总是被调用,不管是什么,也不管是链中的每个对象。

你打算什么时候让它们虚拟化?为什么?


当前回答

当您希望不同的析构函数在通过基类指针删除对象时遵循正确的顺序时,析构函数的虚拟关键字是必需的。例如:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

如果基类析构函数是虚拟的,那么对象将按顺序被析构函数(首先是派生对象,然后是基)。如果基类析构函数不是虚拟的,那么只有基类对象会被删除(因为指针是基类“base*myObj”)。因此派生对象将存在内存泄漏。

其他回答

当您希望不同的析构函数在通过基类指针删除对象时遵循正确的顺序时,析构函数的虚拟关键字是必需的。例如:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

如果基类析构函数是虚拟的,那么对象将按顺序被析构函数(首先是派生对象,然后是基)。如果基类析构函数不是虚拟的,那么只有基类对象会被删除(因为指针是基类“base*myObj”)。因此派生对象将存在内存泄漏。

只要类是多态的,就将析构函数设为虚拟。

还要注意,在没有虚拟析构函数时删除基类指针将导致未定义的行为。我最近学到的东西:

C++中重写删除应该如何操作?

我已经使用C++多年了,但我还是设法自杀了。

虚拟构造函数是不可能的,但虚拟析构函数是可能的。让我们做个实验。。。。。。。

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

上述代码输出以下内容:

Base Constructor Called
Derived constructor called
Base Destructor called

派生对象的构造遵循构造规则,但当我们删除“b”指针(基指针)时,我们发现只有基析构函数被调用。但这绝不能发生。为了做适当的事情,我们必须使基析构函数虚拟化。现在让我们看看以下情况:

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

输出变化如下:

Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called

因此,基指针的销毁(对派生对象进行分配!)遵循销毁规则,即首先是派生指针,然后是基指针。另一方面,没有什么像虚拟构造函数。

将所有析构函数都设为虚拟,除非你有充分的理由不这样做。

否则会发生这样的邪恶:

假设您有一个包含Apple和Orange对象的Fruit指针数组。

从Fruit对象集合中删除时,除非~Fruit()是虚拟的,否则无法调用~Apple()和~Orange()。

正确完成示例:

#include <iostream>
using namespace std;
struct Fruit { // good
  virtual ~Fruit() { cout << "peel or core should have been tossed" << endl; } 
};
struct Apple:  Fruit { virtual ~Apple()  {cout << "toss core" << endl; } };
struct Orange: Fruit { virtual ~Orange() {cout << "toss peel" << endl; } };

int main() { 
  Fruit *basket[]={ new Apple(), new Orange() };
  for (auto fruit: basket) delete fruit;
};

正品产出量

toss core
peel or core should have been tossed
toss peel
peel or core should have been tossed

错误示例:

#include <iostream>
using namespace std;
struct Fruit { // bad 
  ~Fruit() { cout << "peel or core should have been tossed" << endl; } 
};
struct Apple:  Fruit { virtual ~Apple()  {cout << "toss core" << endl; } };
struct Orange: Fruit { virtual ~Orange() {cout << "toss peel" << endl; } };

int main() { 
  Fruit *basket[]={ new Apple(), new Orange() };
  for (auto fruit: basket) delete fruit;
};

不良输出

peel or core should have been tossed
peel or core should have been tossed

(注意:为了简洁起见,我使用了struct,通常使用class并指定public)