设为首页 加入收藏

TOP

C++对象模型――Default Constructor的建构操作(第二章)(一)
2015-11-21 00:56:48 来源: 作者: 【 】 浏览:9
Tags:对象 模型 Default Constructor 建构 操作 第二章

第2章 构造函数语意学 (The Semantics of Constructor)

关于C++,最常听到的一个抱怨就是,编译器背着程序员做了太多事情.Conversion运算符就是最常被引用的一个例子.

2.1 Default Constructor的建构操作

C++ Annotated Reference Manual (ARM)指出default constructors ...在需要的时候被编译器产生出来.关键字眼是在需要的时候.被谁需要?做什么事情?看看下面这段程序代码:
class Foo {
public:
    int val;
    Foo *pnext;
};

void foo_bar() {
    Foo bar;
    if (bar.val || bar.pnext)
        // ... do something
}
在这个例子中,正确的程序语意是要求Foo有一个default constructor,可以将它的两个members初始化为0,上面这段代码不符合ARM所述的在需要的时候.其间的差别在于一个是程序的需要,一个是编译器的需要.上述代码不会合成出一个default constructor.
那么 什么时候才会合成出一个default constructor呢?当编译器需要它的时候!此外,被合成出来的constructor只执行编译器所需的行动,也就是说,即使有需要为class Foo合成一个default constructor,那个constructor也不会将两个data members val和pnext初始化为0.为了让上一段代码正确执行,class Foo的设计者必须提供一个显式的default constructor,将两个members适当地初始化.
C++ Standard已经修改了ARM的说法,虽然其行为事实上仍然相同.C++ Standard指出对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被隐式声明出来....一个被隐式声明的default constructor将是一个trivial constructor...

带有 Default constructor的Member Class Object

如果一个class没有任何constructor,但它内含一个member object,而后者有default constructor,那么这个class的implicit default constructor就是nontrivial,编译器需要为此class合成出一个default constructor,不过这个合成操作只有在constructor真正需要被调用时才会发生.
于是出现一个有趣的问题: 在C++各个不同的编译模块中,编译器如何避免合成出多个default constructor(譬如说一个是为A.C档合成,另一个为B.C档合成)?解决的办法是把合成的default constructor,copy constructor,destructor,assignment copy operator都以inline方式完成.一个inline函数有静态链接(static linkage),不会被档案以外者看到.如果函数太复杂,不适合做成inline,就会合成出一个explicit non-inline static实体.
例如,下面的程序片段中,编译器为class Bar合成一个default constructor:
class Foo {
public:
    Foo();
    Foo(int)
    ...
};
class Bar {
public:
    Foo foo;
    char *str;
};
void foo_bar() {
    Bar bar;    //Bar::foo必须在此处初始化
                //Bar::foo是一个member object.而其class Foo拥有defautl constructor
    if (str) {
        ...
    }
};
被合成的Bar default constructor内含必要的代码,能够调用class Foo的default constructor来处理member object Bar::foo,但它并不产生任何代码来初始化Bar::str.将Bar::foo初始化是编译器的责任,将Bar::str初始化是程序员的责任,被合成的default constructor可能如下:
// Bar的default constructor可能被这样合成
// 被member foo调用class Foo的default constructor
inline Bar::Bar() {
    // C++伪代码
    foo.Foo::Foo();
}

?

注意,被合成的default constructor只满足编译器的需要,而不是程序的需要,为了让这个程序片段能够正确执行,字符指针str也需要被初始化.假设程序员经由下面的default constructor提供了str的初始化操作:
// 程序员定义的default constructor
Bar::Bar() { str = 0; }
现在程序的需求获得满足了,但是编译器还需要初始化member object foo.由于default constructor已经被显式定义,编译器没有办法合成第二个.
编译器采取行动:如果 class A内含一个或一个以上的member class objects,那么 class A的每一个constructor必须调用每一个member classes的default constructor,编译器会扩张已存在的constructors,在其中安插一些码,使得user code在被执行之前,先调用必要的default constructors.沿续前一个例子,扩张后的constructors可能像这样:
// 扩张后的default constructor
// C++伪代码
Bar::Bar() {
    foo.Foo::Foo();    // 附加上complier code
    str = 0;        // 显式user code
}
如果有多个class member objects都要求constructor初始化操作,将如何呢? C++语言要求以member objects在class中的声明次序来调用各个constructors.这一点由编译器完成,它为每一个constructor安插程序代码,以member声明次序调用每一个member所关联的default constructor.这些码被安插在显式user code之前.如果有如下所示三个classes:
class Dopey {
public:
    Dopey();
};
class Sneezy {
public:
    Sneezy(int);
    Sneezy();
};
class Bashful {
public:
    Bashful();
};
    以及一个 class Snow_White:
class Snow_White {
public:
    Dopey dopey;
    Sneezy sneezy;
    Bashful bashful;
private:
    int numb
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇静态代理与动态代理 下一篇数据结构之---C++语言实现图的十..

评论

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