作为通常的原则,如果一个类定义了虚函数,那么它的析构函数就应当是virtual的。因为定义了虚函数则隐含着:这个类会被继承,并且会通过基类的指针指向子类对象,从而得到多态性。这个类可能会被继承,并且会通过基类的指针指向子类对象”,因此基类的析构函数是否为虚将决定子类的对象是否被析构。
#include <iostream.h>
struct A
{
virtual ~A() {cout<<"~A()\n";}
};
struct B: public A
{
~B() {cout<<"~B()\n";}
};
void main()
{
A* p = new B;
delete p;
}
如果 A 的析构函数不是virtual的,那么此时就不是先调用B的析构函数再调用A的析构函数。 输出为:
~A();
如果 A 的析构函数为virtual,则先~B(),再~A(),输出为:
~B();
~A();
类如果会被派生的话,析构函数一般都应该定义为virtual的,主要不是防止内存泄露,而是为了正确的析构。如果是个封闭类(即不再被派生),就不要定义为virtual的。虚函数毕竟耗费较大的。
不用virtual 的几种情况:
- 作为非公有基类。仅作为 private base class 使用的 class 不需要使用虚拟析构函数。
- 不作为接口使用的基类。
- 如果你可以保证这个类不被public继承(private/protected继承的话,在非friend函数/类中就无法用基类指针指向派生类了)
- 如果它的所有派生类(包括派生类的派生类)的析构函数都是trivial的(这里的trivial指的是在程序员的层次什么事也不做)
- 如果不需要用基类的指针指向派生类的对象
在这五种情况下,不把析构函数声明为virtual都是可以的,何况效率会高一些——但前提是你得保证前提的成立——不过这些保证常常是很难100%的:谁能保证别人在派生你的类的时候,析构函数是trivial的,或者别人不用你提供的基类的指针指向派生类对象?这些常常是很难得到保证的。
声明基类的析构函数为virtual并非总是为了防止memory leak 另外这也只是作为一般的原则(基类中有虚函数则把其析构函数声明为virtual)。如果你的析构函数什么事也不作,从效果上来说,不声明为virtual也无妨。
为什么内联函数,构造函数,静态成员函数不能为virtual函数?
(1) 内联函数
内联函数是在编译时期展开,而虚函数的特性是运行时才动态联编,所以两者矛盾,不能定义内联函数为虚函数。
(2) 构造函数
构造函数用来创建一个新的对象,而虚函数的运行是建立在对象的基础上。在构造函数执行时,对象尚未形成,所以不能将构造函数定义为虚函数。
(3) 静态成员函数
静态成员函数属于一个类而非某一对象,没有this指针,它无法进行对象的判别。
分享到:
相关推荐
C++析构函数使用virtual的原因
C++中基类的析构函数为什么要用virtual虚析构函数.pdf
c++ virtual 虚析构函数及虚函数的详细例子.rar
//析构函数做成员函数 }; Base::~Base()//成员函数实现 { cout; } class Derived:public Base { public: Derived(); ~Derived(); private: int *p; }; Derived::Derived() { p=new int(0);//从堆上分配一个int型...
本文给大家介绍了C++中确定基类有虚析构函数的方法。
析构函数的功能是在该类对象消亡之前进行一些必要的清理工作,析构函数最好都是virtual的。首先解释一下虚构函数和指针之间是如何交互的,以及虚析构函数的具体含义。例如以下代码,其中SomeClass是含有非virtual析...
C++中的虚析构函数到底什么时候有用的,什么作用呢。 一.虚析构函数的作用 总的来说虚析构函数是为了避免内存泄露,而且是当子类中会有指针成员变量时才会使用得到的。也就说虚析构函数使得在删除指向子类对象的基类...
○2先不将析构函数声明为virtual,在main函数中另设一个指向Circle类对象的指针变量,使它指向变量,使它指向grad1。运行程序,分析结果。 ○3不作第②点的修改而将析构函数声明为virtual,运行程序,分析结果。 (2...
当通过父类指针delete一个子类对象时,构造的行为未定义 在父类的析构函数前加virual,使用之成为虚析构函数 父类的析构函数在子类的析构函数执行...如果一个类中有virtual函数,那么该类就应该提供一个virual析构函数。
代码如下:#include class base{public: base() { std::cout<<std::endl;... virtual ~base() { std::cout<<std::endl; std::cout<<“base distructor”<<std::endl; func
3. 父类的析构函数是非虚的,但是子类的析构函数是虚的,delete子类指针(指向该子类对象)[特殊情况,参见题5],会调用父类的析构函数(正确)//任何情况下删除子类都会调用到父类的析构函数 4.对于下面的类CA,...
(2) 当析构函数设置为virtual时(一般只需在顶级父类中声明即可),其调用的将会是真实对象的destructor,然后系统一级级地撤销父类的资源 (3)
3、设计构造与析构函数,不要求输出信息,但各位同学可以自己输出并分析各个对象的创建与删除的情况: Book();//将m_ID初始化为0,表示这个一个未赋值对象 virtual ~Book();//无具体的工作 Book(const Book& other...
//消息类的定义 class CMsg : public CObject ... //析构函数 virtual void Serialize(CArchive& ar); //序列化函数 //Attributes public: CString m_strBuf; //字符串成员 BOOL m_bClose; //是否关闭状态
应用程序的plugin实际是实现了一个或多个接口(interface)的动态库。应用程序与plugin之间的... 一个接口(interface)通常声明一个virtual析构函数,一个返回QStringList的virtual函数,以及一个或多个其他virtual函数。
应用程序的plugin实际是实现了一个或多个接口(interface)的动态库。应用程序与plugin之间的... 一个接口(interface)通常声明一个virtual析构函数,一个返回QStringList的virtual函数,以及一个或多个其他virtual函数。
应用程序的plugin实际是实现了一个或多个接口(interface)的动态库。应用程序与plugin之间的... 一个接口(interface)通常声明一个virtual析构函数,一个返回QStringList的virtual函数,以及一个或多个其他virtual函数。
3、设计构造与析构函数,不要求输出信息,但各位同学可以自己输出并分析各个对象的创建与删除的情况: Book();//将m_ID初始化为0,表示这个一个未赋值对象 virtual ~Book();//无具体的工作 Book(const Book& other...
应用程序的plugin实际是实现了一个或多个接口(interface)的动态库。应用程序与plugin之间的... 一个接口(interface)通常声明一个virtual析构函数,一个返回QStringList的virtual函数,以及一个或多个其他virtual函数。
一、接口的定义 有时候,我们得提供一些接口给别人使用。接口的作用,是提供一个与其他...//注意,好要定义此虚析构函数,能够避免其实现不能正常调用析构函数的问题 //提供给外面使用的接口一般采用纯虚函数 vi