可以。不过你得悠着点。当你这样做时,也许你自己都不知道自己在干什么!在构造函数中,虚拟机制尚未发生作用,因为此时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 在构造函数调用后才建立,因而构造函数不可能成为虚函数。从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象...
这就涉及到了在派生类的构造函数中调用基类构造函数的知识点。 首先,每个类都有一个构造函数,它在对象创建时自动执行,用于初始化类的数据成员。当派生类创建时,它的构造函数会先于派生类的任何其他操作调用基类...
2. **成员变量未被正确释放**:由于析构函数遵循从派生类到基类的顺序,如果在基类析构函数中调用了虚函数,那么该函数可能会试图访问已经释放的派生类成员变量,从而导致未定义行为。 3. **潜在的内存泄漏**:如果...
构造函数的调用顺序是从派生类到基类。具体步骤如下: 1. `C`类的构造函数被调用,其中`base(i, j)`调用了`B`类的构造函数。 2. `B`类的构造函数被调用,其中`base(i)`调用了`A`类的构造函数。 3. `A`类的构造函数...
### 构造函数不能声明为虚函数 构造函数在C++中主要用于初始化对象的状态,确保对象在使用前具有有效的初始值。构造函数不能声明为虚函数的原因主要涉及以下几个方面: 1. **对象类型未知**:当创建一个对象时,...
在这个主题中,“聚合中类的构造函数调用顺序”是一个关键概念,尤其是在多层嵌套的对象创建时。理解这个顺序对于编写健壮、无错误的代码至关重要。 首先,让我们明确构造函数的作用。构造函数是类的一个特殊方法,...
然而,当我们尝试在构造函数中调用虚函数时,情况变得稍微复杂一些。下面我们将详细讨论这个问题。 首先,理解C++对象的构造过程至关重要。在创建一个对象时,构造函数会按照继承层次自底向上顺序执行。这意味着先...
2. 如果父类没有无参数的构造函数,或者我们希望在子类构造时传递特定参数给父类,可以在子类的构造函数中使用`super()`关键字显式调用父类的构造函数。这里的`super()`后面可以跟随一个参数列表,与父类构造函数的...
继承作为面向对象编程的一种基本特征,其使用频率... 假设derived 虚继承自base类,那么derivd与base是一种“has a”的关系,即derived类有一个指向base类的vptr。(貌似有些牵强!某些编译器确实如此) 因此虚
当一个对象被创建时,构造函数会被自动调用,它的主要任务是对对象的成员变量进行初始化。构造函数调用的顺序有时是编程中需要考虑的重要问题,特别是在涉及到继承和组合时。本篇文章将深入探讨C++中构造函数的调用...
3. 如果需要在子类构造函数中调用重写后的虚函数,可以考虑使用构造函数初始化列表或者在子类构造体内部完成,以确保对象完全初始化后再调用虚函数。 了解这个原理对于编写复杂继承结构的程序非常重要,因为它可以...
在main函数中,我们使用了多种方式来调用这些函数,例如语句1调用默认构造函数、语句2调用带参数的构造函数、语句5调用复制构造函数等。 在语句4中,我们使用了一个临时对象的方式来调用构造函数,这种方式可以理解...
复制构造函数在创建新对象时调用,而赋值运算符`=`在对象之间进行赋值时调用。确保这两个函数正确实现了深复制,以避免意外地共享或丢失内存资源。 在完成`CArray`类的更新后,`std::map, CArray, int>>`应该能正常...
C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它...
在C++编程中,对象的构造过程涉及到多个层面,包括对象成员构造函数、基类构造函数以及派生类本身的构造函数。理解它们的执行顺序对于编写健壮的代码至关重要。以下将详细阐述这三个构造函数的调用时机和逻辑。 1. ...
每次创建`Student`对象时,构造函数都会自动被调用,完成对象的初始化工作。这里我们还定义了一个`display`成员函数来展示对象的信息: ```cpp void display() { cout ; cout ; cout ; } ``` #### 析构函数 析...
说明了派生类与基类之间的构造函数的调用顺序……
2. **显式调用父类的构造函数**:如果父类中定义了显式构造函数,那么子类的构造函数必须通过`super()`来显式调用父类的一个构造函数。如果不这样做,编译器将报错。 例如: ```java class Base { public Base...
其中,构造函数在定义对象时被调用,析构函数在对象释放时被调用。如果用户没有提供构造函数和析构函数,系统将提供默认的构造函数和析构函数。 1.构造函数 构造函数是一个与类同名的方法,可以没有参数,有一个参数...