深入探索C++对象模型笔记之四 —— 构造函数语意学 (Copy Constructor的建构操作)

本文探讨了C++中三种触发Copy Constructor的情况,并详细解释了默认成员逐个初始化的过程。当类没有显式定义Copy Constructor时,编译器如何处理,以及何时编译器会生成默认的Copy Constructor。文章通过实例分析了具有位逐次拷贝语意的类和不具有此语意的类,以及virtual function和virtual base classes如何影响位逐次拷贝语意。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

有三种情况,会以一个object的内容作为另一个class object的初值。

 

1.当对一个object做明确的初始化操作时

 

2.当object被当做参数交给某个函数时

 

3.当函数传回一个class object时

 

以上三种情况都会导致copy constructor的调用。

 

 

Default Memberwise Initialization

 

如果class没有提供一个explicit copy constructor,编译器会怎么做呢?

 

当class object以“相同的class的另一个object”作为初值时,其内部是以所谓的default memberwise initialization手法完成的,也就是把每一个内建的或派生的data member的值,从某个object拷贝一份到另一个object身上,不过它并不会拷贝其中的member class object,而是以递归的方式(也就是继续调用member class object的copy constructor函数)施行memberwise initialization。

 

这样的操作实际上如何完成?ARM告诉我们:

 

“从概念上而言,对于一个class X,这个操作是被一个copy constructor实现出来...”

 

其中主要的字眼是“概念上”,这个注释又紧跟着一些解释:

 

“一个良好的编译器可以为大部分class objects 产生bitwise copies,因为它们有bitwise copy semantics...”

 

也就是说,“如果一个class未定义出copy constructor,编译器自动为它产生出一个”这句话不对,应该像ARM所说:

 

Default constructor和copy constructor在必要的时候才由编译器产生出来.”

这个句子中的“必要”意指当class不展现bitwise copy semantics时。

C++ Standard 说:

“一个class object可以从两种方式复制得到,一种是被初始化,一种是被指定(assignment),从概念上讲,这两个操作分别是以copy constructor和copy assignment operator 完成的。”

Bitwise Copy Semantics(位逐次拷贝)

#include "Word.h"

Word noun("book");

void foo()

{

    Word verb = noun;

    ...

}

很明显verb是根据noun来初始化。但是在尚未看过class Word的声明之前,我们不可能预测这个初始化操作的程序行为。如果class Word的设计者定义了一个copy constructor,verb的初始化操作会调用它。如果该class没有定义explicit copy constructor,那么是否会有一个编译器合成的实体被调用呢 ?就得视该class是否展现“bitwise copy semantics”而定。

例如:

class Word

{

    public:

        Word(const char*);

        ~Word(delete [] str);

    private:

        int cnt;

        char* str;

}

这种情况下并不需要合成出一个default copy constructor,因为上述声明展现了“default copy semantics”,而verb的初始化操作也不需要以一个函数调用收场。(但是这里会有明显的浅拷贝错误)然而,如果class Word是这样声明:

class Word

{

    public:

        Word(const String&);

        ~Word();

    private:

        int cnt;

        String str;

};

其中String声明了一个explicit copy constructor:

class String

{

   public:

        String(const char*);

        String(const String&);

        ~String();

};

在这个情况下,编译器必须合成出一个copy constructor以便调用member class String object 的copy constructor:

inline Word::Word(const Word& wd)

{

    str.String::String(wd.str);

    cnt = wd.cnt;

}

有一点很值得注意:在这被合成出来的copy constructor中,如整数、指针、数组等等的nonclass members也都会被复制。

种情况下一个class不展现出“bitwise copy semantics”:

1.当class内含一个member object而后者的class声明有一个copy constructor时。(不论是明确声明还是由编译器合成而来)

2.当class继承自一个base class而后者存在一个copy constructor时。(不论是明确 声明还是由编译器合成而来)

3.当class声明了一个或多个virtual function时。

4.当class派生自一个继承串链,其中有一个或多个virtual base classes 时。

下面说明第三条和第四条。

第三条:如果class声明了一个或多个virtual function时,编译器会将一个指向virtual function table的指针(vptr)安插在每一个class object内。很显然,如果编译器对于每一个新产生的class object的vptr不能成功而正确地设好其初值,将导致可怕的后果。因此,当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了。现在,编译器需要合成出一个copy constructor,以求将vptr适当地初始化。

第四条:virtual base class的存在需要特别处理,一个class object如果以另一个object作为初值,而后者有一个virtual base class subobject,那么也会使“bitwise copy semantics”失效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值