为什么析构函数要声明成virtual呢?
来源:互联网 发布:c语言随机生成一个数字 编辑:程序博客网 时间:2024/06/06 05:50
为什么析构函数要声明成virtual呢?
如果一个类要被使用成多态(polymorphic)的,那么这个virtual是必须的。比如:
#include <iostream>
class Animal
{
char* ap;
public:
Animal()
{
ap = new char;
std::cout << "Animal ctor" << std::endl;
}
virtual void foo()
{
std::cout << "Animal::foo" << std::endl;
}
virtual ~Animal()
{
std::cout << "Animal dtor" << std::endl;
delete ap;
}
};
class Dog : public Animal
{
char* dp;
public:
Dog()
{
dp = new char;
std::cout << "Dog ctor" << std::endl;
}
virtual void foo()
{
std::cout << "Dog::foo" << std::endl;
}
virtual ~Dog()
{
delete dp;
std::cout << "Dog dtor" << std::endl;
}
};
int main()
{
Animal* pa = new Dog();
pa->foo();
delete pa;
return 0;
}
delete pa 实际上相当于:
pa->~Animal();
释放pa所指向的内存(或许是free(pa))。
在这里,因为~Animal()是virtual的,尽管是通过Animal类型的指针调用的,根据v-table的信息,~Dog()被正确调用到。如果把virtual属性去掉,那么被调用的是~Animal(),Dog类的构造函数被调用而析构函数未被调用,构造函数中分配的资源没有释放,从而产生了内存泄漏。析构函数缺省声明为virtual,就可以避免这一问题。
可另一个问题是,有时virtual是不需要的。如果一个类不会被继承,比如一个utility类,该类完全是静态方法;或者一些类尽管可能会被继承,但不会被使用成多态的,即除了析构函数外,没有其他的方法是virtual的,这时就可以把virtual属性去掉。
去掉析构函数的virtual属性后,因为该类中没有其他的virtual函数,所以编译时不会生成v-table,这样就节省了编译时间,并减少了最终生成的程序的大小。更重要的是,遵从这一规则,给该类的维护者一个信息,即该类不应被当作多态类使用。
同样,当作一个抽象时,如果你模仿Java的interface,声明了如下的虚基类:
class AbstractBase
{
virtual method1() = 0;
virtual method2() = 0;
};
那么应该给它增加一个空的virtual析构函数:
virtual ~AbstractBase(){}
如果你对COM比较熟悉,可能会注意到,COM interface中并没有这个virutal构造函数。这是因为,COM通过使用引用计数的机制来维护对象。当你使用完一个COM对象,调用Release()时,COM的内部实现检查引用技术是否为零。如果是,则调用
delete this;
因为Release()是virtual的,所以该COM对象对应的正确的派生类被调用,delete this会调用正确的析构函数,达到了使用virtual析构函数的效果。
- 为什么析构函数要声明成virtual呢?
- 为什么析构函数要声明成virtual
- C++里父类的析构函数为什么声明为virtual
- 解释析构函数为什么要声明成虚函数
- 为多态基类声明virtual析构函数
- 为多态基类声明VIRTUAL析构函数
- 为多态基类声明virtual析构函数
- 多态基类应声明 virtual析构函数
- 为多态基类声明virtual析构函数
- 为多态基类声明virtual析构函数
- 多态基类声明virtual析构函数
- 为多态基类声明virtual析构函数
- 析构函数为什么要设置为virtual
- c++ 析构函数为什么要加virtual
- 为什么基类的析构函数要声明成虚函数
- 关于基类为什么要声明析构函数
- 声明类的析构函数是virtual 目的
- 条款07:为多态基类声明virtual析构函数
- LINQ体验(10)--LINQ to SQL语句之开放式并发控制和事务
- 修改和重写
- ado 变量类型长度
- 把SQL Server表中的自动编号ID重新开始排列
- InfoQ: 讨论:测试用例的粒度——粗细之争
- 为什么析构函数要声明成virtual呢?
- 点击按钮复制文本框(Div)值(已测试 支持IE但不支持火狐)
- 命名管道的简单使用
- Oracle中的几个循环
- 近期学习记录
- CommandName属性和CommandArgument属性
- MyEclipse 8.6原来可以绿色的
- 【转】MFC 常用
- .NET中的Drag and Drop操作(二)