您的位置:首页 > 编程语言 > C语言/C++

构造函数不能为虚函数,虚析构函数的作用

2014-09-02 11:42 274 查看

一、构造函数不能为虚函数的理由:

1,从存储空间角度

        虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。

2,从使用角度

        虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。所以构造函数没有必要是虚函数。

虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

3、构造函数不需要是虚函数,也不允许是虚函数,因为创建一个对象时我们总是要明确指定对象的类型,尽管我们可能通过实验室的基类的指针或引用去访问它

但析构却不一定,我们往往通过基类的指针来销毁对象。这时候如果析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数。

4、从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数  

  从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数

5、当一个构造函数被调用时,它做的首要的事情之一是初始化它的vptr。因此,它只能知道它是“当前”类的,而完全忽视这个对象后面是否还有继承者。 当编译器为这个构造函数产生代码时,它是为这个类的构造函数产生代码- -既不是为基类,也不是为它的派生类(因为类不知道谁继承它)。

    所以它使用的vptr必须是对于这个类的vtable。而且,只要它是最后的构造函数调用,那么在这个对象的生命期内, vptr将 保持被初始化为指向这个vtable, 但如果接着还有一个更晚派生的构造函数被调用,这个构造函数又将设置vptr指向它的 vtable,等.直到最后的构造函数结束。vptr的状态是由被最后调用的构造函数确定的。这就是为什么构造函数调用是从基类到更加派生 类顺序的另一个理由。

        但是,当这一系列构造函数调用正发生时,每个构造函数都已经设置vptr指向它自己的 vtable。如果函数调用使用虚机制,它将只产生通过它自己的vtable的调用,而不是最后的vtable(所有构造函数被 调用后才会有最后的vtable)。

用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明:    

    有下面的两个类:

class ClxBase

{
public:

    ClxBase() {};

    virtual ~ClxBase() {};

    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };

};

class ClxDerived : public ClxBase

{
public:

    ClxDerived() {};

    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; 

    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };

};
    代码

ClxBase *pTest = new ClxDerived;

pTest->DoSomething();
delete pTest;
    的输出结果是:

Do something in class ClxDerived!

Output from the destructor of class ClxDerived!
    这个很简单,非常好理解。

    但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:

Do something in class ClxDerived!
    也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。

    所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。

    当然,并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++