`
shi5jin
  • 浏览: 38044 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

我能从构造函数调用虚函数吗?

阅读更多
可以。不过你得悠着点。当你这样做时,也许你自己都不知道自己在干什么!在构造函数中,虚拟机制尚未发生作用,因为此时overriding尚未发生。万丈高楼平地起,总得先打地基吧?对象的建立也是这样——先把基类构造完毕,然后在此基础上构造派生类。
 
看看这个例子:
	#include<string>
#include<iostream>
using namespace std;

class B {
public:
B(const string& ss) { cout << "B constructor\n"; f(ss); }
virtual void f(const string&) { cout << "B::f\n";}
};

class D : public B {
public:
D(const string & ss) :B(ss) { cout << "D constructor\n";}
void f(const string& ss) { cout << "D::f\n"; s = ss; }
private:
string s;
};

int main()
{
D d("Hello");
}
 
这段程序经编译运行,得到这样的结果:
	B constructor
B::f
D constructor
注意,输出不是D::f 。 究竟发生了什么?f()是在B::B()中调用的。如果构造函数中调用虚函数的规则不是如前文所述那样,
而是如一些人希望的那样去调用D::f()。那么因为构造函数D::D()尚未运行,字符串s还未初始化,所以当D::f()试图将参数
赋给s时,结果多半是——立马当机。

析构则正相反,遵循从继承类到基类的顺序(拆房子总得从上往下拆吧?),所以其调用虚函数的行为和在构造函数中一样:虚函数此时此刻被绑定到哪里(当然应该是基类啦——因为继承类已经被“拆”了——析构了!),调用的就是哪个函数。

更多细节请见《The Design and Evolution of C++》,13.2.4.2 或者《The C++ Programming Language》第三版,15.4.3 。

有时,这条规则被解释为是由于编译器的实作造成的。[译注:从实作角度可以这样解释:在许多编译器中,直到构造函数调用完毕,vtable才被建立,此时虚函数才被动态绑定至继承类的同名函数。] 但事实上不是这么一回事——让编译器实作成“构造函数中调用虚函数也和从其他函数中调用一样”是很简单的[译注:只要把vtable的建立移至构造函数调用之前即可]。关键还在于语言设计时的考量——让虚函数可以求助于基类提供的通用代码。[译注:先有鸡还是先有蛋?Bjarne实际上是在告诉你,不是“先有实作再有规则”,而是“如此实作,因为规则如此”。]

原文地址:http://www.research.att.com/~bs/bs_faq2.html#vcall

分享到:
评论

相关推荐

    构造函数不能声明为虚函数的原因及分析

    问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数。 2. 从使用角度,虚函数主要用于在信息不全的情况下,能使...

    构造函数不能为虚函数的理由

    从实现上看,vtable 在构造函数调用后才建立,因而构造函数不可能成为虚函数。从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象...

    在派生类的构造函数中调用基类的构造函数

    这就涉及到了在派生类的构造函数中调用基类构造函数的知识点。 首先,每个类都有一个构造函数,它在对象创建时自动执行,用于初始化类的数据成员。当派生类创建时,它的构造函数会先于派生类的任何其他操作调用基类...

    C++箴言:避免析构函数调用虚函数

    2. **成员变量未被正确释放**:由于析构函数遵循从派生类到基类的顺序,如果在基类析构函数中调用了虚函数,那么该函数可能会试图访问已经释放的派生类成员变量,从而导致未定义行为。 3. **潜在的内存泄漏**:如果...

    c#构造函数的调用过程

    构造函数的调用顺序是从派生类到基类。具体步骤如下: 1. `C`类的构造函数被调用,其中`base(i, j)`调用了`B`类的构造函数。 2. `B`类的构造函数被调用,其中`base(i)`调用了`A`类的构造函数。 3. `A`类的构造函数...

    构造函数不能声明为虚函数,析构函数可以声明为虚函数

    ### 构造函数不能声明为虚函数 构造函数在C++中主要用于初始化对象的状态,确保对象在使用前具有有效的初始值。构造函数不能声明为虚函数的原因主要涉及以下几个方面: 1. **对象类型未知**:当创建一个对象时,...

    聚合中类的构造函数调用顺序

    在这个主题中,“聚合中类的构造函数调用顺序”是一个关键概念,尤其是在多层嵌套的对象创建时。理解这个顺序对于编写健壮、无错误的代码至关重要。 首先,让我们明确构造函数的作用。构造函数是类的一个特殊方法,...

    c++ 构造函数中调用虚函数的实现方法

    然而,当我们尝试在构造函数中调用虚函数时,情况变得稍微复杂一些。下面我们将详细讨论这个问题。 首先,理解C++对象的构造过程至关重要。在创建一个对象时,构造函数会按照继承层次自底向上顺序执行。这意味着先...

    Java继承时构造函数的调用

    2. 如果父类没有无参数的构造函数,或者我们希望在子类构造时传递特定参数给父类,可以在子类的构造函数中使用`super()`关键字显式调用父类的构造函数。这里的`super()`后面可以跟随一个参数列表,与父类构造函数的...

    C++ 虚继承对基类构造函数调用顺序的影响

    继承作为面向对象编程的一种基本特征,其使用频率...  假设derived 虚继承自base类,那么derivd与base是一种“has a”的关系,即derived类有一个指向base类的vptr。(貌似有些牵强!某些编译器确实如此)  因此虚

    构造函数调用次序证明

    当一个对象被创建时,构造函数会被自动调用,它的主要任务是对对象的成员变量进行初始化。构造函数调用的顺序有时是编程中需要考虑的重要问题,特别是在涉及到继承和组合时。本篇文章将深入探讨C++中构造函数的调用...

    基类子类虚函数的调用顺序问题

    3. 如果需要在子类构造函数中调用重写后的虚函数,可以考虑使用构造函数初始化列表或者在子类构造体内部完成,以确保对象完全初始化后再调用虚函数。 了解这个原理对于编写复杂继承结构的程序非常重要,因为它可以...

    构造函数和复制构造函数

    在main函数中,我们使用了多种方式来调用这些函数,例如语句1调用默认构造函数、语句2调用带参数的构造函数、语句5调用复制构造函数等。 在语句4中,我们使用了一个临时对象的方式来调用构造函数,这种方式可以理解...

    没有可用的复制构造函数或复制构造函数声明

    复制构造函数在创建新对象时调用,而赋值运算符`=`在对象之间进行赋值时调用。确保这两个函数正确实现了深复制,以避免意外地共享或丢失内存资源。 在完成`CArray`类的更新后,`std::map, CArray, int&gt;&gt;`应该能正常...

    详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它...

    C++\测试 对象成员构造函数、基类构造函数、派生类本身的构造函数 的先后顺序.rar

    在C++编程中,对象的构造过程涉及到多个层面,包括对象成员构造函数、基类构造函数以及派生类本身的构造函数。理解它们的执行顺序对于编写健壮的代码至关重要。以下将详细阐述这三个构造函数的调用时机和逻辑。 1. ...

    包含构造函数和析构函数的C++程序

    每次创建`Student`对象时,构造函数都会自动被调用,完成对象的初始化工作。这里我们还定义了一个`display`成员函数来展示对象的信息: ```cpp void display() { cout ; cout ; cout ; } ``` #### 析构函数 析...

    关于构造函数的调用的小程序

    说明了派生类与基类之间的构造函数的调用顺序……

    构造函数的继承问题 笔记

    2. **显式调用父类的构造函数**:如果父类中定义了显式构造函数,那么子类的构造函数必须通过`super()`来显式调用父类的一个构造函数。如果不这样做,编译器将报错。 例如: ```java class Base { public Base...

    构造函数与析构函数

    其中,构造函数在定义对象时被调用,析构函数在对象释放时被调用。如果用户没有提供构造函数和析构函数,系统将提供默认的构造函数和析构函数。 1.构造函数 构造函数是一个与类同名的方法,可以没有参数,有一个参数...

Global site tag (gtag.js) - Google Analytics