构造函数、析构函数调用虚函数

昨天笔试的时候碰到一个很有意思的题目,大体如下:

class Parent
{
public:
    Parent()
    {
        doit();
    }
    ~Parent()
    {
        doit();
    }
    virtual void doit()
    {
        cout << "I‘m Parent!" << endl;
    }
};
class Child: public Parent
{
public:
    Child()
    {
    }
    ~Child()
    {
    }
    void doit()
    {
        Parent::doit();
        cout << "I‘m Child!" << endl;
    }
};
int main()
{
    Parent *p_base = new Child();
    p_base->doit();
    delete p_base;
    return 0;
}

以前在项目中并没有写过在构造函数中调用虚函数的情况,这一般也是不允许的,增加了代码的复杂度(参见Effective C++ 条款9)。这里谨记C++继承中构造、析构的顺序就不难写出答案:

I‘m Parent!
I‘m Parent!
I‘m Child!
I‘m Parent!

但是我觉得题目应该可以出的更巧妙一些:

class Parent
{
public:
    Parent()
    {
        doit();
    }
    ~Parent()
    {
        doit();
    }
    virtual void doit()
    {
        cout << "I‘m Parent!" << endl;
    }
};
class Child: public Parent
{
public:
    Child()
    {
        doit();
    }
    ~Child()
    {
        doit();
    }
    void doit()
    {
        Parent::doit();
        cout << "I‘m Child!" << endl;
    }
};
int main()
{
    Parent *p_base = new Child();
    p_base->doit();
    delete p_base;
    return 0;
}

如果你写下:

I‘m Parent!
I‘m Parent!
I‘m Child!
I‘m Parent!
I‘m Child!
I‘m Parent!
I‘m Child!
I‘m Parent!

那么,恭喜你,你答错了。因为这个题目根本不会调用子类的析构函数,为啥?因为父类的析构函数是非虚函数,但是main函数中在释放的静态类型指向父类,所以它根本不调用子类的析构函数。这也是为什么在继承体系中一般都需将父类的析构函数设为虚函数(参见Effective C++ 条款7),否则有可能造成内存泄露。

正确答案是:

I‘m Parent!
I‘m Parent!
I‘m Child!
I‘m Parent!
I‘m Child!
I‘m Parent!
I‘m Child!
I‘m Parent!
时间: 2024-10-14 15:52:20

构造函数、析构函数调用虚函数的相关文章

避免构造/析构函数调用虚函数(转)

不要在类的构造或者析构函数中调用虚函数,因为这种调用不会如你所愿,即使成功一点,最后还会使你沮丧不已.如果你以前是一个Java或者C#程序员,请密切注意本节的内容-这正是C++与其它语言的大区别之一. 假设你有一个为股票交易建模的类层次结构,例如买单,卖单,等等.为该类交易建立审计系统是非常重要的,这样的话,每当创建一个交易对象,在审计登录项上就生成一个适当的入口项.这看上去不失为一种解决该问题的合理方法: class Transaction {// 所有交易的基类 public: Transa

为什么构造函数不能为虚函数

1,从存储空间角度 虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的.问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数. 2,从使用角度 虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用.构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀.所以构造函数没有必要是虚函数. 虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变

为什么构造函数不能为虚函数,而析构函数可以为虚函数

1.  构造函数为什么不能为虚函数? a.  存储空间角度: 虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中,需要调用构造函数才可以创建他的值,否则即使开辟了空间,则虚表指针为随机值,不会找到构造函数:若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数——构造函数了. b.  使用上:  从实现上看,vbtl在构造函数调用后才建立,因而构造函数不可能成为虚函数: 虚函数主要是实现多态,在运行时才可以明确调用对象,根据传入的对象类型,来调用

09 构造函数能调用虚函数吗?

[本文链接] http://www.cnblogs.com/hellogiser/p/whether-constructor-can-call-virtual-function.html [题目] 构造函数可以调用虚函数吗?语法上通过吗?语义上可以通过吗? [分析] 构造函数调用虚函数(virtual function),语法上可以通过(程序可以正常执行),但是语义上通不过(执行结果不是我们想要的) 请看以下代码 C++ Code 12345678910111213141516171819202

C++ Daily 《3》----构造函数可否是虚函数

C++ 中构造函数可否是虚函数? 绝不要!! 而且,在构造函数中调用虚函数也是不提倡的行为,因为会引发预想不到的结果. 因为,在 derived class 对象构造的过程中,首先调用的是基类的构造函数,等基类构造结束,才开始构造派生类的部分. 这个过程中,编译器将该对象视为 基类,而非派生类(因为,派生类对象的专属部分还未初始化!!!) 所以,如果在构造函数中调用虚函数,虚函数调用的一定是基类的虚函数,对象的行为就如同是基类的对象一样. 或者可以这么说:这个时候,虚函数不再是 虚函数... 参

转载: 为什么构造函数不能为虚函数

1,从存储空间角度    虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的.问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数.2,从使用角度        虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用.构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀.所以构造函数没有必要是虚函数.虚函数的作用在于通过父类的指针或者引用来调

C++类的构造函数不能为虚函数的原因

C++类的对象构造的时候,首先申请一片内存,然后调用构造函数进行初始化: 我们知道,存在虚函数的话,也会存在一个虚函数表vtable,而虚函数表示在什么时候产生的呢,当然是在调用构造函数之后产生的: 那么问题来了,如果构造函数为虚函数,此时的内存是一片空白,不存在该虚函数表vtable,那么无法找到该构造函数: 所以说,构造函数不能为虚函数. 对于析构函数而言,可以为虚函数,因为此时虚函数表早已建立:并且,常常析构函数都是虚函数.原因就是,通过基类指针在销毁对象的时候,可以正确的识别要销毁对象的

C++中构造函数能调用虚函数吗?(答案是语法可以,输出错误),但Java里居然可以

环境:XPSP3 VS2005 今天黑总给应聘者出了一个在C++的构造函数中调用虚函数的问题,具体的题目要比标题复杂,大体情况可以看如下的代码: [cpp] view plain copy class Base { public: Base() { Fuction(); } virtual void Fuction() { cout << "Base::Fuction" << endl; } }; class A : public Base { public:

为什么构造函数不能是虚函数

首先,我写了一个构造函数用virtual修饰的类A,代码如下: class A { public: virtual A() {} }; 运行结果:(我是在VS下运行的) 可以看出这样的代码编译时是有问题的. 为什么构造函数不能是虚函数呢? 这里你需要知道一个概念,那就是虚函数表vtbl,每一个拥有虚成员函数的类都有一个指向虚函数表的指针.对象通过虚函数表里存储的虚函数地址来调用虚函数. 那虚函数表指针是什么时候初始化的呢?当然是构造函数.当我们通过new来创建一个对象的时候,第一步是申请需要的内