设为首页 加入收藏

TOP

C++对象模型――Default Constructor的建构操作(第二章)(二)
2015-11-21 00:56:48 来源: 作者: 【 】 浏览:5
Tags:对象 模型 Default Constructor 建构 操作 第二章
er; }; 如果Snow_White没有定义default constructor,就会有一个nontrivial constructor被合成出来,依次序调用Dopey、Sneezy、Bashful的default constructor。然而如果Snow_White定义下面这样的default constructor:
// 程序员所写的default constructor
Snow_White::Snow_White() : Sneezy(1024) {
    member = 2048;
}
    它会被扩张为:
// 编译器扩张后的default constructor
// C++伪代码
Snow_White::Snow_White() : Sneezy(1024) {
    // 插入member class object
    // 调用其constructor
    dopey.Dopey::Dopey();
    sneezy.Sneezy::Sneezy(1024);
    bashful.Bashful::Bashful();
    // 显式user mode
    member = 2048;
}
顺序依次为类的成员对象,类的构造函数。

带有Default Constructor的Base Class

如果一个没有任何的constructors的class派生自一个带有default constructor的base class ,那么这个derived class 的default constructor会被视为nontrivial,并因此需要被合成出来,它将调用上一层的base classes的default constructor(根据它们的声明次序),对一个后继派生的 class 而言,这个合成的constructor和一个被明确提供的default constructor没有差异。
如果设计者提供多个constructor,但其中都没有default constructor呢?编译器会扩张现有的每一个constructor,将用以调用所有必要的default constructors的程序代码加进去,它不会合成一个新的default constructor,这是因为其他由user所提供的constructor存在的缘故。如果同时存在带有default constructors的member class objects,那些default constructor也会被调用在——在所有base class constructor都被调用之后。可以看出, 构造是从类层次的最根处开始的,在每一层,首先调用基类构造函数,然后调用成员对象的构造函数

带有一个Virtual Function的Class

另有两种情况,也需要合成出default constructor:
1. class 声明(或继承)一个virtual function
2. class 派生自一个继承串链,其中有一个或更多的virtual base classes.
不管哪一种情况,由于缺乏由user声明的constructors,编译器会详细记录合成一个default constructor的必须信息,以下面这个程序片段为例:
class Widget {
public:
    virtual void flip() = 0;
    // ...
};
void flip(const Widget &widget) {
    widget.flip();
}
// 假设Bell和Whistle都派生自Widget
void foo() {
    Bell b;
    Whistle w;
    flip(b);
    flip(w);
}
下面两个扩张会在编译期间发生:
1. 一个virtual function table(在cfront中被称为vtbl)会被编译器产生出来,内放 class 的 virtual functions 地址。
2. 在每一个 class object中,一个额外的pointer member(也就是vptr)会被编译器合成出来,内含相关的 class vtbl的地址。
此外,widget.flip()的虚拟引发操作(virtual invocation)会被重新改写,以使用widget的vptr和vtbl中的flip()条目:
// widget.flip()的虚拟引发操作(virtual invocation)的转变
(*widget.vptr[1])(&widget)
其中:
1表示flip()在virtual table中的固定索引
&widget代表要交给被调用的某个flip()函数实体的this指针。
为了让这机制发挥功效, 编译器必须为每一个Widget(或其派生类的)object的vptr设定初始值,放置适当的virtual table地址。对于 class 所定义的每一个constructor,编译器会安插一些代码做这样的事情(看5.2节)。对于那些未声明任何 constructor的classes,编译器会为它们合成一个default constructor,以便正确地初始化每一个 class object的vptr.

带有一个Virtual Base Class的Class

Virtual base class 的实现法在不同的编译器之间有极大的差异,然而, 每一种实现法的共通点在于必须使virtual base class 在其每一个derived class object 中的位置,能够于执行期准备妥当,例如下面这段代码中:
class X {
public:
    int i;
};
class A : public virtual X {
public:
    int j;
};
class B : public virtual X {
public:
    double d;
};
class C : public A, public B {
public:
    int k;
};
// 无法在编译时期决定(resolve) pa->X::i的位置
void foo(const A *pa) {
    pa->i = 1024;
}
main()
{
    foo(new A);
    foo(new C);
    // ...
}
编译器无法确定foo()中经由pa而存取的X::i的实际偏移位置,因为pa的真正类型可以改变。 编译器必须改变执行存取操作的那些码,使X::i可以延迟至执行期才决定。原先cfront的做法是靠在derived class object的每一个virtual base classes中安插一个指针完成。所有经由reference或pointer来存取一个virtual base class的操作都可以通过相关指针完成。foo可以被改写如下,以符合这样的实现策略:
// 可能的编译器转换操作
void foo(const A *pa) {
    pa->_vbcX->i = 1024;
}
其中,_vbcX表示编译器所产生的指针,指向virtual base class X.
_vbcX(或编译器所做出的某个东西)是在 class object 建构期间被完成的。对于 class 所定义的每一个constructor,编译器会安插那些允许每一个virtual base class的执行期存取操作的码。如果 class 没有声明任
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇静态代理与动态代理 下一篇数据结构之---C++语言实现图的十..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: