构造函数之语意学

1、  一个类是不是一定有构造函数?

 C++ Annotated ReferenceManual(ARM):构造函数只在需要的时候才会被编译器产生出来。关键字“在需要的时候”,被谁需要?一种是程序需要的时候,一种是变编译器需要的时候。如果程序有需要(如,完成某些非静态数据成员的初始化),那是程序员的责任,程序员需要自己提供构造函数。如果是编译器需要则才会合成出来默认构造函数。编译器不会替程序员完成任何多余的工作。

 

2、  编译器什么情况下产生默认构造函数?产生的默认构造函数做些什么?

1、  类A中包含类B对象的数据成员,且类B带有默认构造函数。编译器将会合成默认构造函数用以调用B的默认构造函数完成类B对象的初始化。

2、  类A拥有一个带有默认构造函数的基类B,编译器将会合成默认构造函数用以调用B的默认构造函数。(无论是明确声明或是被合成而得)

3、  类A中包含一个虚函数时,编译器将会合成默认构造函数用以完成对象模型中虚函数表指针的初始化。

4、  类A拥有一个虚基类时,编译器将合成默认构造函数用以安插“允许每一个虚基类的执行期存取操作”的代码。

备注1:如果程序员已提供默认构造函数,则编译器将所需的操作插入该构造函数完成扩展。

   备注2:C++书籍里常见的错误

1>    任何class如果没有定义默认构造函数,就会被合成出来一个

2>    编译器合成出来的默认构造函数会明确的设定“class内每一个data member的默认值”。

如你所见,没有一个是真的!

 

3、  编译器什么情况下产生复制构造函数?

“在必要的时候”,即class不展现出bitwise copy semantics时。

1、类A中包含类B对象的数据成员,且类B带有复制构造函数。编译器将会合成复制构造函数用以调用B的复制构造函数完成类B对象的初始化。

2、类A拥有一个带有复制构造函数的基类B,编译器将会合成复制构造函数用以调用B的复制构造函数。(无论是明确声明或是被合成而得)

3、类A中包含一个虚函数时,编译器将会合成复制构造函数用以完成对象模型中虚函数表指针的重新设定。

4、类A拥有一个虚基类时,编译器将会合成复制构造函数用以完成对virtual base class的特殊处理(发生在derived class初始化base class的情况下,需要存取基类的操作)。

备注:说的更明白一些同默认构造函数产生的情况类似(一致)。

 

4、  复制构造函数的程序转化。

复制构造函数的调用发生在以下三种情况下:明确的初始化操作、参数的初始化、返回值得初始化。复制构造函数的调用都伴随着程序转化:

1、  明确的初始化操作:

重写每一个定义,其中的初始化操作被剥除;类的复制构造调用操作被安插进去。

 C++ Code 
1
2
3
4
5
6
X x0;
void foo_bar()
{
    X x1(x0);
    //...
}

转化后

 C++ Code 
1
2
3
4
5
void foo_bar()
{
    X x1;   //定义重写,初始化操作被剥除
    x1.X::X(x0);
}

2、  参数的初始化

已知的函数定义:void foo(X x0);

 C++ Code 
1
2
X xx;
foo(xx);

转化后

 C++ Code 
1
2
3
X  __tmp0;
__tmp0.X::X(xx);
foo(__tmp0);

3、  返回值的初始化

添加额外参数,类型是class object的一个reference,用来防治被拷贝构建而得的返回值;在return之前安插一个复制构造的调用操作。

 C++ Code 
1
2
3
4
5
6
X bar()
{
    X xx;
    //处理xx...
    return xx;
}

转化后

 C++ Code 
1
2
3
4
5
6
7
8
void bar(X &__result)
{
    X xx;      //注释:看到没,所谓的NRV优化就是可以将这个变量优化掉,直接处理__result;
    xx.X::X();
    //处理xx...
    __result.X::X(xx);
    return ;
}

所以 X xx=bar();将被转化为 X xx;  bar(xx);

 

4 什么时候一个class不展现出bitwise copy semantics呢?有四种情况:

    1> 当class内含一个member object而后者的class声明有一个copy constructor时(不论是被class设计者明确地声明,或是是被编译器合成)。
    2> 当class继承自一个bass class而后者存在有一个copy constructor时(再次强调,不论是被明确声明或被合成而得)。
    3> 当class声明了一个或多个virtual functions时。这种情况编译器会为每一个class object安插一个vptr指针,这样该类就不展现bitwise semantics了。现在,编译器要合成一个copy constructor,以求将vptr适当地初始化。
注意:
    ① 当一个类对象以另一个相同的类对象作为初值时,都可以直接靠"bitwise copy semantics"完成。
    ② 当一个base class object以其derived class的object内容初始化操作时,其vptr复制操作必须保证安全。 合成出来的基类copy constructor会明确设定object的vptr指向基类的virtual table, 而不是直接从右手边的派生类中将其vptr现值拷贝过去
    4> 当class派生自一个继承串联,其中有一个或多个virtual base classes时
    virtual base class 的存在需要特别处理。一个class object如果以另一个object作为初值,而后者有一个virtual class subobject,那么会使"bitwise copy semantics"失效。
    每一个编译器对于虚拟继承的支持承诺,都表示必须让“derived class object中的virtual base class subobject位置”在执行期备妥。维护“位置的完整性”是编译器的责任bitwise copy semantics可能会破坏这个位置所以编译器必须在它自己合成出来的copy constructor中做出仲裁(一部分工作是安插一些码设定virtual base class pointer/offset的初值或简单地确定它没有被摸消)。

5、成员初始化列表(member initializationlist)

         以下四种情况必须使用成员初始化列:

1、  当初始化一个引用类型的数据成员时;

2、  当初始化一个const数据成员时;

3、  当调用基类的带参构造函数时;

4、  当调object类型的数据成员的带参构造函数时;

前两者因为要求定义时初始化,所以必须明确的在初始化队列中给它们提供初值。后两者因为需要显式的调用它们的带参构造函数而非默认构造函数在定义时初始化它们。

备注:说清楚点就是所有需要在定义时初始化的都需要使用初始化列表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值