精华内容
下载资源
问答
  • 2020-11-19 16:05:38

    题目:

    写程序定义一个基类BaseClass,从它派生出类DerivedClass,在BaseClass中声明虚析构函数,在主函数中将一个动态分配的Derived的对象地址赋给一个BaseClass指针,然后通过指针释放对象空间.观察程序运行的过程.在BaseClass中的析构函数换成非虚构函数,再观察此时程序的运行过程.

    非虚析构函数

    #include <iostream>
    
    using namespace std;
    
    class BaseClass{
    public:
        BaseClass(){cout<<"construct BaseClass"<<endl;}
         ~BaseClass(){ cout<<"destruct BaseClass"<<endl;}
    };
    
    class Derived:public BaseClass{
    public:
        Derived(){        cout<<"construct DerivedClass"<<endl;
    }
         ~Derived(){cout<<"destruct DerivedClass"<<endl;}
    };
    
    int main()
    {
        BaseClass *b=new Derived;
        delete b;
        return 0;
    }
    

    输出结果

    construct BaseClass
    construct DerivedClass
    destruct BaseClass
    

    虚析构函数的输出结果为

    construct BaseClass
    construct DerivedClass
    destruct DerivedClass
    destruct BaseClass
    

    分析

    类DerivedClass的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。
    所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
    当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

    更多相关内容
  •  实现多态时, 当用基类指针操作派生类, 析构时候防止只析构基类而不析构派生类。 2、例子:  (1)、    #include  using namespace std;  class Base{  public: Base() {};  ~Base() {cout <&...
  • class base{public:  base() { std::cout<<std::endl; std::cout<<“base constructor”<<std::endl; func1(); std::cout<<std::endl; }  virtual ~base() { std::cout<<...
  • C++的类,构造函数用于初始化对象及相关操作,...继承层次,基类的析构函数一般建议声明为虚函数。下面通过一个例子来说明下基类析构函数声明为虚函数的必要性。#include using namespace std;class base {p...
    aff728971998cf8763b2e7238214f421.png

    C++的类中,构造函数用于初始化对象及相关操作,构造函数是不能声明为虚函数的,因为在执行构造函数前对象尚未完成创建,虚函数表还不存在。

    析构函数用于销毁对象完成时相应资源的释放工作,析构函数可以被声明为虚函数。在继承层次中,基类的析构函数一般建议声明为虚函数。

    下面通过一个例子来说明下基类析构函数声明为虚函数的必要性。

    #include using namespace std;class base {public:    base() {        cout << "base constructor" << endl;        int *b = new int[5];    }    ~base() {        cout << "base destructor" << endl;        delete[] b;    }private:    int *b;};class derived : public base {public:    derived() {        cout << "derived constructor" << endl;        int *d = new int[8];    }    ~derived() {        cout << "derived destructor" << endl;        delete[] d;    }private:    int *d;};int main(){    base *pBase = new derived;    cout << "---" << endl;    delete pBase;    return 0;}

    运行结果:

    base constructorderived constructor---base destructor

    上面定义了两个类:一个基类base,一个派生类derived。基类和派生类都分别定义了各自的构造函数和析构函数。

    基类和派生类中各有一个int型指针成员变量:

    • 在基类的构造函数中,给指针变量b分配了5个int型空间;基类的析构函数用于将b所指的空间释放掉;
    • 在派生类的构造函数中,给指针成员变量d分配了8个int型空间;派生类的析构函数用于释放掉d指针所指向的存储空间。

    在主函数中创建一个基类类型的指针pBase,指向一个派生类对象,之后释放掉pBase指针所指向的对象的存储空间。

    观察程序的运行结果,:

    • 首先,基类的构造函数被调用(base constructor);
    • 其次,派生类的构造函数也被调用(derived constructor);
    • 最后,基类的析构函数被调用(base destructor)。

    但是最后却没有调用派生类的析构函数,这样会导致d指针所指向的整型存储空间不会被释放,从而造成内存泄漏。

    为了解决这个问题,需要将基类的析构函数声明为虚函数。修改如下:

    virtual ~base() {    cout << "base destructor" << endl;    delete[] b;}

    运行结果:

    base constructorderived constructor---derived destructorbase destructor

    将基类的析构函数声明为虚函数之后,派生类的析构函数也自动成为虚析构函数,在主函数中基类指针pBase指向的是派生类对象,当delete释放pBase指针所指向的存储空间时,

    • 首先执行派生类的析构函数(derived destructor);
    • 然后执行基类的析构函数(base destructor)。
    展开全文
  • //析构函数做成员函数 }; Base::~Base()//成员函数实现 { cout<<"Base destructor"; } class Derived:public Base { public: Derived(); ~Derived(); private: int *p; }; Derived::Derived() { p=new int(0);//...
  • 本文给大家介绍了C++确定基类有虚析构函数的方法。
  • 本文实例讲述了PHP构造函数与析构函数用法。分享给大家供大家参考,具体如下: 实例化一个新对象时,构造方法析构方法都会被自动调用,若有继承则会使用父类的对应方法。 析构方法情况下会被调用: ① ...
  • C++中虚析构函数和纯虚函数的作用

    千次阅读 多人点赞 2017-12-26 23:38:10
    虚析构函数为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为delete一个抽象类指针时候,必须要通过函数找到真正的析构函数class Base { public: Base(){} ...

    一. 虚析构函数

    为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候,必须要通过虚函数找到真正的析构函数。

    class Base
    {
    public:
       Base(){}
       virtual ~Base(){}
    };
    
    class Derived: public Base
    {
    public:
       Derived(){};
       ~Derived(){};
    }
    
    void foo()
    {
       Base *pb;
       pb = new Derived;
       delete pb;
    } 

    这是正确的用法,会发生动态绑定,它会先调用Derived的析构函数,然后是Base的析构函数。

    如果析构函数不加virtual,delete pb只会执行Base的析构函数,而不是真正的Derived析构函数。
    因为不是virtual函数,所以调用的函数依赖于指向静态类型,即Base。

    二. 纯虚析构函数

    现在的问题是,我们想把Base做出抽象类,不能直接构造对象,需要在其中定义一个纯虚函数。如果其中没有其他合适的函数,可以把析构函数定义为纯虚的,即将前面的CObject定义改成:

    class Base
    {
    public:
       Base(){}
       virtual ~Base()= 0
    };

    可是,这段代码不能通过编译,通常是link错误,不能找到~Base()的引用(gcc的错误报告)。这是因为,析构函数、构造函数和其他内部函数不一样,在调用时,编译器需要产生一个调用链。也就是,Derived的析构函数里面隐含调用了Base的析构函数。而刚才的代码中,缺少~Base()的函数体,当然会出现错误。

    这里面有一个误区,有人认为,virtual f()=0这种纯虚函数语法就是没有定义体的语义。
    其实,这是不对的。这种语法只是表明这个函数是一个纯虚函数,因此这个类变成了抽象类,不能产生对象。我们完全可以为纯虚函数指定函数体 。通常的纯虚函数不需要函数体,是因为我们一般不会调用抽象类的这个函数,只会调用派生类的对应函数。这样,我们就有了一个纯虚析构函数的函数体,上面的代码需要改成:

    class Base
    {
    public:
       Base()
       {
       }
       virtual ~Base() = 0; //pure virtual
    };
    
    Base::~Base()//function body
    {
    } 

    从语法角度来说,不可以将上面的析构函数直接写入类声明中(内联函数的写法)。这或许是一个不正交化的地方。但是这样做的确显得有点累赘。

    这个问题看起来有些学术化,因为一般我们完全可以在Base中找到一个更加适合的函数,通过将其定义为没有实现体的纯虚函数,而将整个类定义为抽象类。但这种技术也有一些应用,如这个例子:

    class Base  //abstract class
    {
    public:
       virtual ~Base(){};//virtual, not pure
       virtual void Hiberarchy() const = 0;//pure virtual
    };
    
    void Base::Hiberarchy() const //pure virtual also can have function body
    {
       std::cout <<"Base::Hiberarchy";
    }
    
    class Derived : public Base
    {
    public:
       Derived(){}
       virtual void Hiberarchy() const
       {
           CB::Hiberarchy();
           std::cout <<"Derived::Hiberarchy";
       }
       virtual void foo(){}
    };
    
    
    int main(){
       Base* pb=new Derived();
       pb->Hiberarchy();
       pb->Base::Hiberarchy();
       return 0;
    } 

    在这个例子中,我们试图打印出类的继承关系。在根基类中定义了虚函数Hiberarchy,然后在每个派生类中都重载此函数。我们再一次看到,由于想把Base做成个抽象类,而这个类中没有其他合适的方法成员可以定义为纯虚的,我们还是只好将Hiberarchy定义为纯虚的。

    另外,可以看到,在main中有两种调用方法,第一种是普通的方式,进行动态链接,执行虚函数,得到结果”Derived::Hiberarchy”;第二种是指定类的方式,就不再执行虚函数的动态链接过程了,结果是”Base::Hiberarchy”。

    通过上面的分析可以看出,定义纯虚函数的真正目的是为了定义抽象类,而并不是函数本身。与之对比,在java中,定义抽象类的语法是 abstract class,也就是在类的一级作指定。

    虚函数小结

    1) 虚函数是动态绑定的,也就是说,使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数。这是虚函数的基本功能,就不再解释了。

    2) 构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还没有构造好, 多态是被disable的。

    3) 析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。

    4) 将一个函数定义为纯虚函数,实际上是将这个类定义为抽象类,不能实例化对象。

    5) 纯虚函数通常没有定义体,但也完全可以拥有。

    6) 析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。

    7) 非纯的虚函数必须有定义体,不然是一个错误。

    8) 派生类的override虚函数定义必须和父类完全一致。除了一个特例,如果父类中返回值是一个指针或引用,子类override时可以返回这个指针(或引用)的派生。例如,在上面的例子中,在Base中定义了 virtual Base* clone(); 在Derived中可以定义为 virtual Derived* clone()。可以看到,这种放松对于Clone模式是非常有用的。

    展开全文
  •  我们大家都知道,C++ ,当一个对象销毁时,析构函数是用来对类对象对象成员进行释放内存做一些其他的cleanup操作。析构函数靠~符号来区分,~ 出现 析构函数名字的前面, 当我们去定义一个 虚析构函数...

    原因是因为多态的存在

           我们大家都知道,在C++ 中,当一个对象销毁时,析构函数是用来对类对象和对象成员进行释放内存和做一些其他的cleanup操作。析构函数靠~符号来区分,~ 出现在 析构函数名字的前面,  当我们去定义一个 虚析构函数时,你只需要简单的的在~符号前面 加一个  virtual标志就可以了。
           为什么需要将析构函数声明为 虚函数,我们最好用几个例子来验证一下,我们首先以一个 不使用虚析构函数的例子开始,然后我们使用一个使用析构函数的例子。一旦看到了其中的区别,你就会明白 为什么需要将析构函数声明为虚函数。让我们开始看一下代码。

           Example  without a Virtual Destructor:

    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
        Base(){ cout<<"Constructing Base\n";}
    
       // this is a destructor:
       ~Base(){ cout<<"Destroying Base\n";}
    };
    
    class Derive: public Base
    {
    public:
       Derive(){ cout<<"Constructing Derive\n";}
    
       ~Derive(){ cout<<"Destroying Derive\n";}
     };
    
    int main()
    {
    	Base *basePtr = new Derive();
        delete basePtr;
        return 0;
    }
    运行结果如下:


    根据上面的输出,我们可以看到当我们新建一个指向Deriver类型对象指针的时候,构造函数按照由基类到派生类的顺序依次调用,但是当我们删除指向Deriver 的基类指针时, Deriver类中的析构函数没有被调用,而是只调用了Base的析构函数。(原因可参考:编译期绑定运行期绑定

    当我们将基类的析构函数声明为虚函数时

      Example with a Virtual Destructor:

    我们需要做的就是修改Base 类中的析构函数,在~前面加上virtual关键字。

    class Base
    {
    public:
        Base(){ cout<<"Constructing Base\n";}
    
       // this is a destructor:
       virtual ~Base(){ cout<<"Destroying Base\n";}
    };
    改变之后,运行结果如下:


    我们在基类中将析构函数声明为虚函数,就表示在使用析构函数时,是采用运行期绑定的。那么delete basePtr的时候运行的是根据basePtr指向的具体类型来调用析构函数。


    如果你在 派生类中 分配了 内存空间的话,没有将基类的析构函数声明为虚析构函数,很容易发生内存泄漏事件。
    例子:

    #include <iostream>
    using namespace std;
    
    class Base
    {
    public:
    	Base(){ data = new char[10];}
    
     // this is a destructor:
    
    	~Base(){ cout << "destroying Base data[]";delete []data;}
    private:
    	char *data;
    };
    
    class Derive: public Base
    {
    public:
    	Derive(){ D_data = new char[10];}
    
    	~Derive(){ cout << "destroying Derive data[]";delete []D_data;}
    private:
    	char *D_data;
     };
    
    main()
    {
       Base *basePtr = new Derive();
       delete basePtr;
       return 0;
    }
    我们在基类和派生类中都个分配了 10个 字节的空间,这些空间应该由程序员来释放,如果没有释放掉的话,就会造成内存泄漏。

    运行结果如下:


    我们可以看到只删除了基类的分配的空间,这个时候派生类的对象的空间没有删除,内存泄漏。

    另外一个例子:

    #include <iostream>
    using namespace std;
    
    class CBase
    {
    public:
        CBase(){data = new char[64];}
        ~CBase(){delete [] data;}
    private:
        char *data;
    };
    
    class CFunction
    {
    public:
        CFunction(){};
        ~CFunction(){};
    };
    
    class CFunctionEx : public CFunction
    {
    public:
        CFunctionEx(){};
        ~CFunctionEx(){};
    private:
        CBase m_cbase;
    };
    
    void main()
    {
    	CFunction *pCFun = new CFunctionEx;
    	delete pCFun;
    }
    
    这里CfunctionEx和Cfunction中本身并没有分配内存,应该不会有内存泄漏。和上例一样当删除pCFun时,它只调用了Cfunction的析构函数而没调用CfunctionEx的析构函数,但CfunctionEx本身并没分配内存。所以发生内存泄露的地方是m_cbase,因为它是CBase的实例且是CfunctionEx成员变量,当CfunctionEx的析构函数没有被调用时,当然m_cbase的析构函数也没有被调用,所以CBase中分配的内存被泄漏。
    解决以上问题的方法很简单,就是使基类Cfunction的析构函数为虚函数就可以了。

    这样就得出一个结论:当你的基类的析构函数不为虚函数的话,其子类中所有的成员变量的类中分配的内存将可能泄漏。将基类的析构函数设为virtual型,则基类的所有派生类的析构函数都会自动设置为virtual型,这保证了任何情况下,都不会出现由于析构函数没有被调用而导致的内存泄漏。

    展开全文
  • #include using namespace std;class Base{ public: Base(){}; ~Base(){ cout这是Base类的析构函数"; }; virtual void DoSomething(){ cout这是Base类的DoSomething"<<en
  • 虚析构函数2. 函数函数表函数联系到多态,多态联系到继承。函数的作用3. 纯虚函数4.函数与纯虚函数的区别5. 基类 1. 析构函数 实现多态时,当用基类操作派生类,析构时防止只析构基类而不析构...
  • 定义一个基类BaseClass,从它派生出类DerivedClass,BaseClass中定义虚析构函数, //主程序中将一个DerivedClass的对象地址赋给一个BaseClass的指针,并输出构造析构信息。 #include"iostream" using namespace ...
  • 因为析构函数声明为虚函数,将使所有派生类的析构函数自动成为函数,如果程序显示的用了delete运算符删除一个对象,而delete运算符操作对象指向派生类对象的基类的指针,则系统会调用相应类的析构函数。...
  • C.35: A base class destructor should be either public and ...基类的析构函数要么是公开的函数,要么是保护的非函数 Reason(原因) To prevent undefined behavior. If the destructor is publi...
  • 关于虚析构函数

    2021-11-26 17:42:18
    当你写一个需要扩展的类或者子类,或者说只要你允许一个类可以被继承,那么应该将该类的析构函数声明为虚析构函数,否则该父类无法被安全地扩展。 如果不使用虚析构函数,当子类的对象指针传参给一个函数,而这个...
  • 虚析构函数

    2018-07-15 18:30:25
    如果有派生类,析构函数声明为虚函数,这是为了防止新手犯错误。如果基类指针指向了派生类的对象,析构时,只会调用基类析构函数,没有派生类的析构函数class Base { public: Base(){ } ~Base(){ // 应...
  • 一、声明虚析构函数的原因(1)先考虑没有声明虚析构函数的代码:#include using namespace std;class Base{ public: Base(int x,int y):a(x),b(y){ cout<<"Base Construction"; } virtual void display()=0;
  • ,构造函数用于初始化对象及相关操作,构造函数是不能声明为虚函数的,因为执行构造函数前对象尚未完成创建,虚函数表并不存在,此时就无法去查询虚函数表因此也就无法得知该调用哪一个构造函数了。...
  • 实现多态时,当用基类操作派生类,析构时防止只析构基类而不析构派生类的状况...所以通常是将析构函数声明为虚函数。一个基类的函数一旦声明为虚函数,那么不管你是否加上virtual修饰符,它所有派生类中都成为
  • 为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为delete一个抽象类指针时候,必须要通过函数找到真正的析构函数class Base { public: Base(){} virtual ~Bas....
  • C++虚析构函数

    2021-04-23 21:09:43
    C++虚析构函数 一般来说,用new运算符动态生成的对象都是通过delete指向它的指针来释放的,例如 Base1 *p = new Base1; delete p; 但是我们有时会让一个基类指针指向用new运算符动态生成的派生类对象,例如以下...
  • 虚析构函数的作用

    2017-08-03 21:27:47
    什么基类的析构函数最好声明为虚函数?基类的函数声明为虚函数有什么作用?什么将基类的析构函数声明为虚函数就可以很好的避免内存泄漏这一问题呢?
  • C++析构函数的作用是:当一个对象被销毁时,调用析构函数对类对象对象成员进行释放内存清除工作。什么要将基类的析构函数声明为虚函数: 请看下面的代码示例:#include using namespace std;class Base...
  • 虚析构函数详解

    千次阅读 2020-04-15 17:46:48
    众所周知,virtual函数(函数),是实现动态调度(运行时多态) 概念:当基类指针指向子类对象时候,函数能实现运行...1、当基类析构函数不设置为虚析构 class BaseClass { public: BaseClass() {} ~BaseCl...
  • 详解C++析构函数

    2021-05-22 03:24:13
    简介析构函数(Destructors),是对象的成员函数,没有...编译器会自动创建默认的析构函数,通常都没有问题,但是当我们动态分配了内存空间时,我们需要手段的回收这块空间,防止内存溢出。就像这样class Strin...
  • 什么基类析构函数声明为虚析构函数? 解答 用对象指针来调用一个函数,有以下两种情况: 如果是函数,会调用派生类的版本。如果是非函数,会调用指针所指类型的实现版本。 析构函数也会...
  • class Base { public: ~Base(); //不是虚函数 }; Base::~Base() { cout<< "Base destructor" << endl; } class Derived: public Base{ public: Derived(); ~Derived(); //不是虚函数 priva
  • 虚函数–特征:virtual class A //基类 { public: virtual void f(){...} //虚函数 }; class B:public A //派生类 { public: virtual void f(){...} }; 测试主函数 int main //主函数 { B x; A *p; p = &...
  • 对于虚析构函数的理解

    万次阅读 多人点赞 2017-08-06 16:09:00
    首先,对于虚析构函数,那就得说下构造函数析构函数了。 构造函数:进行初始化成员变量的函数。 析构函数对象生命周期结束的时候,完成资源的回收清理。 对于析构,就是在析构函数前加virtual关键字,那么...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,005
精华内容 12,002
关键字:

在baseclass中声明虚析构函数,分未声明为虚析构函数和声明为析构函数两种情形; (2

友情链接: EP4CE6.rar