虚析构函数的使用(转载!)

一、当你的类准备给别人继承时要提供虚析构函数
考虑下面例子:
class A
{
public:
 A(){cout << "In A constructor" << endl;}
 ~A(){cout << "In A destructor" << endl;}
};

class B : public A
{
public:
 B()
 {
 cout << "In B constructor" << endl;
 m_p = new char[10];
 }
 ~B()
 {
 cout << "In B destructor" << endl;
 if (m_p) delete [] m_p;
 }
private:
 char *m_p;
};

int main(int argc, char* argv[])
{
 //printf("Hello World!/n");
 A *p = new B;
 delete p;

 return 0;
}
输出结果:
In A constructor
In B constructor
In A destructor

并没有调用B的析构函数,new出来的内存没有及时回收造成内存泄漏。
要解决这个问题,只要将A的析构函数定义为虚函数:~A(){cout << "In A destructor" << endl;}。为什么定义为虚函数就能解决呢?我是这样理解的:
象其它虚构函数一样,~B()重定义(overridden)了~A(),这样指向派生类的指针就能根据运行时的状态调用B的析构函数了。这里又有一个问题:为什么还会调用A的析构函数呢?我只能理解为析构函数是一个特殊的函数,由系统维护其机制。就像B.~A()是错误而B.~B()(虽然逻辑上不对,但语法上是正确的,编译运行完全没问题)是正确的一样。

 

     

虚函数和普通成员函数的区别,是虚函数放在虚函数表中,通过对象的this指针找到该类的虚函数表,然后调用。C++即采用此机制实现多态。如果是普通函数,每个函数的地址是死的。所以用A类的对象调用析构函数时只能调到A的析构。如果是虚函数,则会通过指针找到B的析构函数,而B继承自A,还会调用A的析构函数。

因此,虚析构函数用在有虚函数的继承。否则,你那样的用法也是不正确的。A *p=new B 如果没有虚函数,会导致截断。

二、


#if CODE1
#include <iostream>
//给出一个没有虚拟析构函数的基类
class Base
{
public:
        Base()
        {
                std::cout<<"Base::Base()"<<std::endl;
        }
        ~Base()
        {
                std::cout<<"Base::~Base()"<<std::endl;
        }
};
//给出一个没有虚拟析构函数的继承类
class
Derived:public Base
{
public:
        Derived()
        {
                std::cout<<"Derived::Derived()"<<std::endl;
        }
        ~Derived()
        {
                std::cout<<"Derived::~Derived()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
        {
//堆栈变量的情况
                std::cout << "----------[Derived d;]----------" << std::endl;
                Derived d;
        }
        {
//堆内存分配,并且删除派生类对象指针的情况
                std::cout << "----------[Derived *pd = new Derived();]----------" << std::endl;
                Derived *pd = new Derived();
                std::cout << "----------[delete pd;]----------" << std::endl;
                delete pd;
//这里会调用派生类和基类的析构函数,虽然派生类和基类的析构函数不是虚拟的
        }
        {
//堆内存分配,并且删除基类对象指针的情况
                std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
                Base *pb = new Derived();
                std::cout << "----------[delete pb;]----------" << std::endl;
                delete pb;
//这里就不会调用派生类的析构函数
        }
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Derived d;]----------
Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()
----------[Derived *pd = new Derived();]----------
Base::Base()
Derived::Derived()
----------[delete pd;]----------
Derived::~Derived()
Base::~Base()
----------[Base *pb = new Derived();]----------
Base::Base()
Derived::Derived()
----------[delete pb;]----------
Base::~Base()
*******************************************************************************/

#if 0
    从上面的讨论中可以看出,只有最后一种情况才会对析构函数是否是虚拟的有要求,在
后面的讨论中将会只讨论这种情况。
#endif
#endif//CODE1
#if CODE2
#include <iostream>
//给出一个没有虚拟析构函数的基类
class
Base
{
public:
        Base()
        {
                std::cout<<"Base::Base()"<<std::endl;
        }
        ~Base()
        {
                std::cout<<"Base::~Base()"<<std::endl;
        }
};
//给出一个有虚拟析构函数的继承类
class
Derived:public Base
{
public:
        Derived()
        {
                std::cout<<"Derived::Derived()"<<std::endl;
        }
        virtual~Derived()
        {
                std::cout<<"Derived::virtual~Derived()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
       
//堆内存分配,并且删除基类对象指针的情况
        std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
        Base *pb = new Derived();
        std::cout << "----------[delete pb;]----------" << std::endl;
        delete pb;
//这里就不会调用派生类的析构函数
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Base *pb = new Derived();]----------
Base::Base()
Derived::Derived()
----------[delete pb;]----------
Base::~Base()
*******************************************************************************/

#if 0
    很明显并没有调用派生类的虚拟析构函数。
#endif
#endif//CODE2
#if CODE3
#include <iostream>
//给出一个有虚拟析构函数的基类
class
Base
{
public:
        Base()
        {
                std::cout<<"Base::Base()"<<std::endl;
        }
        virtual~Base()
        {
                std::cout<<"Base::virtual~Base()"<<std::endl;
        }
};
//给出一个没有虚拟析构函数的继承类
class
Derived:public Base
{
public:
        Derived()
        {
                std::cout<<"Derived::Derived()"<<std::endl;
        }
        ~Derived()
        {
                std::cout<<"Derived::~Derived()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
       
//堆内存分配,并且删除基类对象指针的情况
        std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
        Base *pb = new Derived();
        std::cout << "----------[delete pb;]----------" << std::endl;
        delete pb;
//这里会调用派生类的析构函数
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Base *pb = new Derived();]----------
Base::Base()
Derived::Derived()
----------[delete pb;]----------
Derived::~Derived()
Base::virtual~Base()
*******************************************************************************/

#if 0
    很明显调用派生类的虚拟析构函数。但是这里值得注意的是:派生类的析构函数并不
是虚拟的,为了说明派生类的虚拟析构函数是否一定要是虚拟的,还需要更多的测试。
#endif
#endif//CODE3
#if CODE4
#include <iostream>
//给出一个有虚拟析构函数的基类
class
Base
{
public:
        Base()
        {
                std::cout<<"Base::Base()"<<std::endl;
        }
        virtual~Base()
        {
                std::cout<<"Base::virtual~Base()"<<std::endl;
        }
};
//给出一个有虚拟析构函数的继承类
class
Derived:public Base
{
public:
        Derived()
        {
                std::cout<<"Derived::Derived()"<<std::endl;
        }
        virtual~Derived()
        {
                std::cout<<"Derived::virtual~Derived()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
       
//堆内存分配,并且删除基类对象指针的情况
        std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
        Base *pb = new Derived();
        std::cout << "----------[delete pb;]----------" << std::endl;
        delete pb;
//这里会调用派生类的析构函数
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Base *pb = new Derived();]----------
Base::Base()
Derived::Derived()
----------[delete pb;]----------
Derived::virtual~Derived()
Base::virtual~Base()
*******************************************************************************/

#if 0
    很明显调用派生类的虚拟析构函数。通过CODE3和CODE4可以看出只要基类的析构函数
是虚拟的则派生类的析构函数不论是否是虚拟的都将在删除基类指针的时候调用派生类的
析构函数。不过CODE3和CODE4仅仅讨论的是一次一重继承的情况。对于多次多重继承的情
况则需要另外的单独讨论。还是先讨论多次继承的情况,然后再讨论多重继承的情况,最
后讨论多次多重继承的情况。
#endif
#endif//CODE4
#if CODE5
//测试多次继承的情况
#include <iostream>
//给出一个有虚拟析构函数的基类
class
Base
{
public:
        Base()
        {
                std::cout<<"Base::Base()"<<std::endl;
        }
        virtual~Base()
        {
                std::cout<<"Base::virtual~Base()"<<std::endl;
        }
};
//给出一个没有虚拟析构函数的继承类
class
Derived1:public Base
{
public:
        Derived1()
        {
                std::cout<<"Derived1::Derived1()"<<std::endl;
        }
        ~Derived1()
        {
                std::cout<<"Derived1::~Derived1()"<<std::endl;
        }
};
//给出另一个没有虚拟析构函数的继承类
class
Derived2:public Derived1
{
public:
        Derived2()
        {
                std::cout<<"Derived2::Derived2()"<<std::endl;
        }
        ~Derived2()
        {
                std::cout<<"Derived2::~Derived2()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
       
//堆内存分配,并且删除基类对象指针的情况
        {
                std::cout << "----------[Base *pb = new Derived2();]----------" << std::endl;
                Base *pb = new Derived2();
                std::cout << "----------[delete pb;]----------" << std::endl;
                delete pb;
//这里会调用派生类的析构函数
        }
        {
                std::cout << "----------[Base *pd1 = new Derived1();]----------" << std::endl;
                Derived1 *pd1 = new Derived2();
                std::cout << "----------[delete pd1;]----------" << std::endl;
                delete pd1;
//这里会调用派生类的析构函数
        }
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Base *pb = new Derived2();]----------
Base::Base()
Derived1::Derived1()
Derived2::Derived2()
----------[delete pb;]----------
Derived2::~Derived2()
Derived1::~Derived1()
Base::virtual~Base()
----------[Base *pd1 = new Derived1();]----------
Base::Base()
Derived1::Derived1()
Derived2::Derived2()
----------[delete pd1;]----------
Derived2::~Derived2()
Derived1::~Derived1()
Base::virtual~Base()
*******************************************************************************/

#if 0
    很明显调用了所有派生类的虚拟析构函数。但是这里值得注意的是:派生类的析构函
数并不是虚拟的。可以看出只要有基类的析构函数是虚拟的,那么所有的派生类不管是否
明确的写了虚拟析构函数,派生了的析构函数一定是虚拟的。
#endif
#endif//CODE5
#if CODE6
//测试多重继承的情况
#include <iostream>
//给出一个有虚拟析构函数的基类
class
Base1
{
public:
        Base1()
        {
                std::cout<<"Base1::Base1()"<<std::endl;
        }
        virtual~Base1()
        {
                std::cout<<"Base1::virtual~Base1()"<<std::endl;
        }
};
class Base2
{
public:
        Base2()
        {
                std::cout<<"Base2::Base2()"<<std::endl;
        }
        virtual~Base2()
        {
                std::cout<<"Base2::virtual~Base2()"<<std::endl;
        }
};
//给出一个没有虚拟析构函数的继承类
class
Derived:public Base1,public Base2
{
public:
        Derived()
        {
                std::cout<<"Derived::Derived()"<<std::endl;
        }
        ~Derived()
        {
                std::cout<<"Derived::~Derived()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
       
//堆内存分配,并且删除基类对象指针的情况
        {
                std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
                Base1 *pb = new Derived();
                std::cout << "----------[delete pb;]----------" << std::endl;
                delete pb;
//这里会调用派生类的析构函数
        }
        {
                std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
                Base2 *pb = new Derived();
                std::cout << "----------[delete pb;]----------" << std::endl;
                delete pb;
//这里会调用派生类的析构函数
        }
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Base *pb = new Derived();]----------
Base1::Base1()
Base2::Base2()
Derived::Derived()
----------[delete pb;]----------
Derived::~Derived()
Base2::virtual~Base2()
Base1::virtual~Base1()
----------[Base *pb = new Derived();]----------
Base1::Base1()
Base2::Base2()
Derived::Derived()
----------[delete pb;]----------
Derived::~Derived()
Base2::virtual~Base2()
Base1::virtual~Base1()
*******************************************************************************/

#if 0
    很明显调用了所有派生类的析构函数和基类的析构函数。另外还可以看出只要有了多重
继承的任意一个基类的指针都可以通过这个指针将整个对象删除。另外还需要考虑一下某个
基类的析构函数不是虚拟的情况。
#endif
#endif//CODE6
#if CODE7
//测试多重继承的情况
#include <iostream>
//给出一个有虚拟析构函数的基类
class
Base1
{
public:
        Base1()
        {
                std::cout<<"Base1::Base1()"<<std::endl;
        }
        virtual~Base1()
        {
                std::cout<<"Base1::virtual~Base1()"<<std::endl;
        }
};
class Base2
{
public:
        Base2()
        {
                std::cout<<"Base2::Base2()"<<std::endl;
        }
        ~Base2()
        {
                std::cout<<"Base2::~Base2()"<<std::endl;
        }
};
//给出一个没有虚拟析构函数的继承类
class
Derived:public Base1,public Base2
{
public:
        Derived()
        {
                std::cout<<"Derived::Derived()"<<std::endl;
        }
        ~Derived()
        {
                std::cout<<"Derived::~Derived()"<<std::endl;
        }
};
//下面的测试代码
int
main()
{
       
//堆内存分配,并且删除基类对象指针的情况
        {
                std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
                Base1 *pb = new Derived();
                std::cout << "----------[delete pb;]----------" << std::endl;
                delete pb;
//这里会调用派生类的析构函数
        }
        {
                std::cout << "----------[Base *pb = new Derived();]----------" << std::endl;
                Base2 *pb = new Derived();
                std::cout << "----------[delete pb;]----------" << std::endl;
                delete pb;
//这里不会调用派生类的析构函数
        }
        return 0;
}

//运行结果如下所示:
/*******************************************************************************
----------[Base *pb = new Derived();]----------
Base1::Base1()
Base2::Base2()
Derived::Derived()
----------[delete pb;]----------
Derived::~Derived()
Base2::~Base2()
Base1::virtual~Base1()
----------[Base *pb = new Derived();]----------
Base1::Base1()
Base2::Base2()
Derived::Derived()
----------[delete pb;]----------
Base2::~Base2()
*******************************************************************************/

#if 0
    很明显,是否调用所有派生类的析构函数和基类的析构函数,取决于删除的基类指针的
基类的析构函数是否是虚拟的,如果指针的析构函数是虚拟的,那么将会调用所有的派生类
的析构函数和所有的其它基类的析构函数,否则就会象上面的第二种情况一样仅仅删除了基
类自身而已,导致对象没有完全释放。
#endif
#endif//CODE7

 

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值