何时使用虚拟析构函数?
我对大多数面向对象理论有了深刻的理解,但让我困惑的是虚拟析构函数。
我认为,无论对于链中的每个对象而言,析构函数都会被调用。
你什么时候想让它们变成虚拟的,为什么?
当您可以通过指向基类的指针删除派生类的实例时,虚拟析构函数非常有用:
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
在这里,你会注意到我没有声明Base的析构函数是virtual
。 现在,让我们看看下面的代码片段:
Base *b = new Derived();
// use b
delete b; // Here's the problem!
由于Base的析构函数不是virtual
而b
是指向Derived
对象的Base*
,所以delete b
具有未定义的行为:
[在delete b
]中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义 。
在大多数实现中,对析构函数的调用将像任何非虚拟代码一样被解析,这意味着基类的析构函数将被调用,但不是派生类的析构函数,从而导致资源泄漏。
总而言之,当基本类的析构函数意图被多态操纵时,总是使它们变成virtual
。
如果要防止通过基类指针删除实例,则可以使基类析构函数受保护而不受虚拟; 通过这样做,编译器不会让你在基类指针上调用delete
。
您可以从Herb Sutter的本文中了解更多关于虚拟性和虚拟基类析构函数的知识。
在多态基类中声明析构函数。 这是Scott Meyers的Effective C ++中的第7项。 Meyers继续总结说,如果一个类有任何虚拟函数,它应该有一个虚拟析构函数,并且没有被设计为基类或不被设计为多态使用的类不应该声明虚拟析构函数。
虚拟构造函数是不可能的,但虚拟析构函数是可能的。 让我们试验....
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Calledn";
}
~Base(){
cout << "Base Destructor calledn";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor calledn";
}
~Derived1(){
cout << "Derived destructor calledn";
}
};
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 Calledn";
}
virtual ~Base(){
cout << "Base Destructor calledn";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor calledn";
}
~Derived1(){
cout << "Derived destructor calledn";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
输出更改如下:
Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called
所以基指针的销毁(对派生对象进行分配!)遵循破坏规则,即首先派生,然后基地。 另一方面,对于构造函数来说,没有什么像虚拟构造函数。
链接地址: http://www.djcxy.com/p/20779.html