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

【本文链接】

http://www.cnblogs.com/hellogiser/p/whether-constructor-can-call-virtual-function.html

【题目】

构造函数可以调用虚函数吗?语法上通过吗?语义上可以通过吗?

【分析】

构造函数调用虚函数(virtual function),语法上可以通过(程序可以正常执行),但是语义上通不过(执行结果不是我们想要的)

请看以下代码

C++ Code


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 
/*
version: 1.0
author: hellogiser
blog: http://www.cnblogs.com/hellogiser
date: 2014/9/29
*/

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
    Base()
    {
        Foo();
    }

virtual void Foo()
    {
        cout << "Base::Foo " << 1 << std::endl;
    }
};

class Derived : public Base
{
public:
    Derived() : Base(), m_pData(new int(2)) {}
    ~Derived()
    {
        delete m_pData;
    }

virtual void Foo()
    {
        cout << "Derived::Foo " << *m_pData << std::endl;
    }
private:
    int *m_pData;
};

void test()
{
    Base *p = new Derived();
    delete p;
}

int main()
{
    test();
    return 0;
}
/*
Base::Foo 1
*/

执行结果是Base::Foo 1

这表明第19行执行的的是Base::Foo()而不是Derived::Foo(),也就是说:虚函数在构造函数中“不起作用”。为什么?

  当实例化一个派生类对象时,首先进行基类部分的构造,然后再进行派生类部分的构造。即创建Derived对象时,会先调用Base的构造函数,再调用Derived的构造函数。当在构造基类部分时,派生类还没被完全创建,从某种意义上讲此时它只是个基类对象。即当Base::Base()执行时Derive对象还没被完全创建,此时它被当成一个Base对象,而不是Derive对象,因此Foo绑定的是Base的Foo。

  C++之所以这样设计是为了减少错误和Bug的出现。假设在构造函数中虚函数仍然“生效”,即Base::Base()中的Foo();所调用的是Derive::Foo()。当Base::Base()被调用时派生类中的数据m_pData还未被正确初始化,这时执行Derive::Foo()将导致程序对一个未初始化的地址解引用,得到的结果是不可预料的,甚至是程序崩溃(访问非法内存)。

  总结来说:基类部分在派生类部分之前被构造,当基类构造函数执行时派生类中的数据成员还没被初始化。如果基类构造函数中的虚函数调用被解析成调用派生类的虚函数,而派生类的虚函数中又访问到未初始化的派生类数据,将导致程序出现一些未定义行为和bug。

构造函数直接调用纯虚函数(pure virtual function),编译会报错: unresolved externals

【代码】

C++ Code


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 
/*
version: 1.0
author: hellogiser
blog: http://www.cnblogs.com/hellogiser
date: 2014/9/29
*/

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
    Base()
    {
        Foo();
    }

virtual void Foo() = 0; // pure virtual function
};

class Derived : public Base
{
public:
    Derived() : Base(), m_pData(new int(2))
    {
    }
    ~Derived()
    {
        delete m_pData;
    }

virtual void Foo()
    {
        cout << "Derived::Foo " << *m_pData << std::endl;
    }
private:
    int *m_pData;
};

void test()
{
    Base *p = new Derived();
    delete p;
}

int main()
{
    test();
    return 0;
}

如果编译器都能够在编译或链接时识别出这种错误调用,那么我们犯错的机会将大大减少。只是有一些比较不直观的情况,编译器是无法判断出来的。这种情况下它可以生成可执行文件,但是当程序运行时会出错,因为此时Base::Foo只声明没定义。

构造函数间接调用纯虚函数(pure virtual function)(即:构造函数调用普通函数,但是普通函数又调用了纯虚函数),编译阶段不会报错,可以生成可执行文件,但是运行会出错,因为纯虚函数没有定义。

【代码】

C++ Code


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 
/*
version: 1.0
author: hellogiser
blog: http://www.cnblogs.com/hellogiser
date: 2014/9/29
*/

#include "stdafx.h"
#include <iostream>
using namespace std;

class Base
{
public:
    Base()
    {
        Function();
    }

void Function()
    {
        Foo(); // normal function calls pure virtual function
    }

virtual void Foo() = 0; // pure virtual function
};

class Derived : public Base
{
public:
    Derived() : Base(), m_pData(new int(2))
    {
    }
    ~Derived()
    {
        delete m_pData;
    }

virtual void Foo()
    {
        cout << "Derived::Foo " << *m_pData << std::endl;
    }
private:
    int *m_pData;
};

void test()
{
    Base *p = new Derived();
    delete p;
}

int main()
{
    test();
    return 0;
}

结论:永远不要在类的构造或者析构过程中调用虚函数,因为这样的调用永远不会沿类继承树往下传递到子类中去。

【参考】

http://www.cnblogs.com/carter2000/archive/2012/04/28/2474960.html

http://blog.csdn.net/hxz_qlh/article/details/14089895

时间: 2024-08-12 12:44:52

09 构造函数能调用虚函数吗?的相关文章

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:

C++构造函数中调用虚函数

谈谈关于构造函数中调用虚函数的情况,仅讨论单继承,不考虑虚拟继承和多重继承. 测试平台:VS2013 + Win7X64 一个例子: #include <stdlib.h> #include <stdio.h> class Base { private: int __data; public: Base() { this->Func(); } public: virtual void Func() { printf("Base::Func"); } };

C++面试题1:构造函数和虚构函数中能否调用虚函数?

C++面试题1:构造函数和虚构函数中能否调用虚函数? 构造函数跟虚构函数里面都可以调用虚函数,编译器不会报错. C++ primer中说到最好别用 由于类的构造次序是由基类到派生类,所以在构造函数中调用虚函数,虚函数是不会呈现出多态的 类的析构是从派生类到基类,当调用继承层次中某一层次的类的析构函数时意味着其派生类部分已经析构掉,所以也不会呈现多态 因此如果在基类中声明的纯虚函数并且在基类的析构函数中调用之,编译器会发生错误. class Base { public: Base() { Fuct

EC笔记,第二部分:9.不在构造、析构函数中调用虚函数

9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #include <iostream> using namespace std; class cls1{ public: cls1(){ newMake(); }; ~cls1(){ deleteIt(); }; virtual void newMake(){ cout<<"cls1 make"<<endl; } virtual void deleteIt()

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

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

Item 9:在析构/构造时不要调用虚函数 Effective C++笔记

Item 9: Never call virtual functions during construction or destruction. 父类构造期间,对虚函数的调用不会下降至子类.如果这并非你的意图,请不要这样做! 这个问题阿里实习面试曾经问到过,看这篇文章: 2014阿里巴巴面试经历 看Scott Meyers举的例子: class Transaction { // base class for all public: // transactions Transaction(){ /

在构造函数和析构函数中调用虚函数------新标准c++程序设计

在构造函数和析构函数中调用虚函数不是多态,因为编译时即可确定调用的是哪个函数.如果本类有该函数,调用的就是本类的函数:如果本类没有,调用的就是直接基类的函数:如果基类没有,调用的就是间接基类的函数,以此类推.例如: #include<iostream> using namespace std; class A { public: virtual void hello(){cout<<"A::hello()"<<endl;} virtual void

为什么构造函数内部不能调用虚函数

其实也不是不能调用,调用自然是可以的,只不过构造函数中的虚函数不具有多态性,不能达到我们想要的效果. class Base { public: Base() { Fuction(); } virtual void Fuction() { cout << "Base::Fuction" << endl; } }; class A : public Base { public: A() { Fuction(); } virtual void Fuction() {

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

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