请问有没有人用过virtual的析构函数?

tianlm 2003-10-16 03:52:35
我在一个程序里面见到了虚的析构函数,
不知道有没有人用过?

有什么特别么?
...全文
22 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
jhyu 2003-10-17
  • 打赏
  • 举报
回复
这是一个很好的准则,大多数情况都适用。但不幸的是,当类里没有虚函数的时候,也会带来非虚析构函数问题。 例如,条款13里有个实现用户自定义数组下标上下限的类模板。假设你(不顾条款m33的建议)决定写一个派生类模板来表示某种可以命名的数组(即每个数组有一个名字)。
template<class t> // 基类模板
class array { // (来自条款13)
public:
array(int lowbound, int highbound);
~array();
private:
vector<t> data;
size_t size;
int lbound, hbound;
};
template<class t>
class namedarray: public array<t> {
public:
namedarray(int lowbound, int highbound, const string& name);
...
private:
string arrayname;
};
如果在应用程序的某个地方你将指向namedarray类型的指针转换成了array类型的指针,然后用delete来删除array指针,那你就会立即掉进“不确定行为”的陷阱中。
namedarray<int> *pna =
new namedarray<int>(10, 20, "impending doom");
array<int> *pa;
...

pa = pna; // namedarray<int>* -> array<int>*
...
delete pa; // 不确定! 实际中,pa->arrayname
// 会造成泄漏,因为*pa的namedarray
// 永远不会被删除

现实中,这种情形出现得比你想象的要频繁。让一个现有的类做些什么事,然后从它派生一个类做和它相同的事,再加上一些特殊的功能,这在现实中不是不常见。namedarray没有复位义array的任何行为——它继承了array的所有功能而没有进行任何修改——它只是增加了一些额外的功能。但非虚析构函数的问题依然存在(还有其它问题,参见m33)
最后,值得指出的是,在某些类里声明纯虚析构函数很方便。纯虚函数将产生抽象类——不能实例化的类(即不能创建此类型的对象)。有些时候,你想使一个类成为抽象类,但刚好又没有任何纯虚函数。怎么办?因为抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以方法很简单:在想要成为抽象类的类里声明一个纯虚析构函数。
这里是一个例子:
class awov { // awov = "abstract w/o
// virtuals"
public:
virtual ~awov() = 0; // 声明一个纯虚析构函数

};
这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:
awov::~awov() {} // 纯虚析构函数的定义
这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。
可以在函数里做任何事,但正如上面的例子一样,什么事都不做也不是不常见。如果是这种情况,那很自然地会想到将析构函数声明为内联函数,从而避免对一个空函数的调用所产生的开销。这是一个很好的方法,但有一件事要清楚。
因为析构函数为虚,它的地址必须进入到类的vtbl(见条款m24)。但内联函数不是作为独立的函数存在的(这就是“内联”的意思),所以必须用特殊的方法得到它们的地址。条款33对此做了全面的介绍,其基本点是:如果声明虚析构函数为inline,将会避免调用它们时产生的开销,但编译器还是必然会在什么地方产生一个此函数的拷贝。
freewing 2003-10-16
  • 打赏
  • 举报
回复
一般来说,类中定义了virtual成员函数的话同时就应该定义virtual析构函数
maojincxj 2003-10-16
  • 打赏
  • 举报
回复
楼上的对
Wolf0403 2003-10-16
  • 打赏
  • 举报
回复
to Hot_Forever(用钱砸死我吧):
应该是保证正确调用到“子类的析构函数”,不是父类。
Wolf0403 2003-10-16
  • 打赏
  • 举报
回复
试试这个就知道了。
#include <iostream>
using namespace std;

struct Base
{
Base(){}
~Base(){cout << "~Base" << endl;}
};

struct Derived: public Base
{
~Derived(){cout << "~Derived()" << endl;}
};

struct BaseV
{
BaseV(){}
virtual ~BaseV(){cout << "~BaseV" << endl;}
};

struct DerivedV: public BaseV
{
~DerivedV(){cout << "~DerivedV()" << endl;}
};

int main()
{
Base * pb = new Derived;
delete pb; // 没有调用 ~Derived
cout << "===========================" << endl;
BaseV * pv = new DerivedV;
delete pv; // 调用了 ~DerivedV。这就是虚拟函数的作用。
return 0;
}
blue_coco 2003-10-16
  • 打赏
  • 举报
回复
eg:
class Base{
.......
int *a;
Base(){
a = new int[10];
}
virtual ~Base(){
delete []a;
}
}

class Type :Base{
.......
int *b;
Type(){
b = new int[10];
}
~Type(){
delete []b;
}
}

main()
{
Type b;
Base *a;

a = (Base*)b;
delete a;
}

代码可能有错, 请包含。
当释放a时会先释放Type 的b
Hot_Forever 2003-10-16
  • 打赏
  • 举报
回复
如果你的类要被继承的话,最好就把析构函数定义为虚拟
这样做的好处就是确保在删除一个指向子类对象的父类指针的时候能够正确的调用到父类的析构函数,保证资源释放

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧