构造函数语义学——Copy Constructor的建构操作

?
在三种情况下,会以一个object的内容作为另一个class object的初值:

  1. object明确初始化

    123
    class {...};X x;X xx = x;
  2. object被当作参数交与某个函数
    12345
    extern void foo(X x);void bar(){    X xx;    foo(xx);}
  3. 函数返回值是一个class object
    12345
    X foo_bar(){    X xx;    ...    return xx;}

如果开发者已经明确定义了一个copy constructor如下:

123
//copy constructor可以是多参数,其中有一个参数是其class typeX::X(const X& x);Y::Y(const Y& y);

那么在大部分情况下,当class object以另一个同类实体作为初值时,上述constructor会被调用,这可能会导致一个暂时性class object的产生或程序代码发生改变(或二者都有)。


Default Memberwise Initialization

?
如果函数并没有提供一个explicit copy constructor,那么其拷贝同类型对象的操作由default memberwise initialization完成,其执行策略为:对每一个内建或派生的data member的值,从某一个object拷贝到另一个object。不过它不会拷贝其中的member class object,而是实施递归式的memberwise initialization(对每一个对象依次执行default memberwise initialization)。

实例

考虑如下class声明:

1234567
class String{public:    ...//不存在explicit copy constructorprivate:    char *str;    int len;};

String object的default memberwise initialization发生于这种情况:

12
String noun("book");String verb = noun;

其完成方式类似于依次各别设定每一个member:

12
verb.str = noun.str;verb.len = noun.len;

如果String是另一个class的member:

1234567
class Word{public:     ...//不存在explicit copy constructorprivate:    int _occurs;    String _word;};

那么一个Word object的default memberwise initialization会拷贝其内建的member _occurs,然后对String member执行递归式memberwise initialization。

如果开发者没有为class指定copy constructor,那么编译器也会生成implicit的声明与定义。类似于Default constructor,C++亦把copy constructor区分为trivial与non-trivial两种,只有non-trivial的实体才会被合成于程序之中。决定一个copy constructor是否为trivial的依据是class是否表现出所谓的“bitwise copy semantics”。


Bitwise Copy Semantics

1234567

Word noun("book");

void foo(){    Word verb = noun;}

很明显verb是根据noun来初始化,但在未了解Word class声明之前,我们无法预测该初始化操作的程序行为。如果开发者定义了copy constructor,那么编译器会调用它。如果没有,编译器会根据class 是否展现出bitwise copy semantics,来合成一个copy constructor。

举例而言,如果Word内部数据仅仅含有内置类型,那么编译器不会合成copy constructor,而是执行bitwise copy。但如果Word内含一个String object,而String class存在一个explicit copy constructor,那么编译器将不得不合成一个copy constructor,以调用member object的copy constructor,在该合成的copy constructor中,内置类型仍然使用bitwise copy。


Bitwise Copy不出现的情况

?
一个class有四种情况不会表现出bitwise copy:

  1. class内含一个member object,而后者有一个copy constructor(无论是开发者指定还是编译器合成)
  2. class继承自一个base class,而后者存在一个copy constructor(无论是开发者指定还是编译器合成)
  3. class声明了一个或多个virtual functions
  4. class存在的继承体系内存在一个或多个virtual base class

在前两种情况下,编译器会将member或base class的copy constructors插入至合成的copy constructors中。


重设定vptr

?
在上一节我们曾经阐述过,含有virtual functions的class会在编译期间构造函数会自发扩张:

  1. class会增加一个vtbl
  2. class object会增加一个vptr

显然,vptr是决定多态机制能否正确运行的关键,当编译器将vptr导入至class之中时,该class不再具备bitwise semantics。编译器需要合成一个copy constructor,将vptr合理初始化。

实例

现有继承体系及class声明如下:

1234567891011121314151617
class ZooAnimal{public:    ZooAnimal();    virtual ~ZooAnimal();    virtual void animate();    virtual void draw();private:    ...//some data};class Bear : public ZooAnimal{public:    Bear();    void animate();    void draw();private:    ...//some data};

ZooAnimal object相互赋值或者Bear object相互赋值都可以通过bitwise copy semantics完成。在这种情况下,vptr保持bitwise copy是安全的。

当一个base class object以其derived class object内容作初始化操作时,其vptr也需要保证安全:

12
Bear B;ZooAnimal Z = B;//sliced

显然,Z的vptr不应该指向Bear的vtbl,也就是说,Base class被合成出来的copy constructor会明确设定object的vptr指向Base Class的vtbl,而非从rhs处执行bitwise copy。


处理Virtual Base Class Subobject

?
一个class object如果以另一个object作为初值,而后者带有一个virtual base class subobject,那么bitwise semantics同样会失效。

在上一节中我们已经编译器需要保证在运行期明确virtual base class subobject的位置。Bitwise copy可能会破坏这个位置。

实例

现有继承体系及class声明如下:

···C++
class Raccoon:public virtual ZooAnimal{
public:
Raccoon();
Raccoon(int val);

private:
…//some data
}

123456789101112131415161718
在开发者撰写的constructor中,编译器会生成: 1. 调用ZooAnimal的default constructor代码 2. Raccoon的vptr初始化代码 3. 定位Raccoon中ZooAnimal subobject代码

编译器会将上述代码插入至两个constructor之内,并且放在开头位置。

当一个class object以其derived class object作为初值时,bitwise失效,因为此时编译器必须判断“后续当程序员试图存取其Base class subobject时程序能否正常运行。考虑如下继承体系与声明:![image_1cchtdv2o1tvlga1o4414a752613.png-20.9kB][3]```C++class RedPanda:public Raccoon{public:    RedPanda();    RedPanda(int val);private:    ...}

现有object分析图如下:

在下述代码中,编译器无法判断bitwise copy是否有效,因为编译器无法了解Raccoon是否指向了一个真正的Raccoon对象:

12
Raccoon *ptr;Raccoon little_critter = *ptr;//可能ptr指向了派生类

总结

?
我们是否可以认为,在bitwise copy完全合理的情况下,应当禁止调用copy constructor以优化程序?这个问题将会在后续章节中讨论。
本节我们讨论了class不再保持bitewise copy semantics的四种情况,在这四种情况下,如果未声明copy constructor,那么编译器为了保证初始化正确,将会合成一个copy constructor。

原文:大专栏  构造函数语义学——Copy Constructor的建构操作

原文地址:https://www.cnblogs.com/wangziqiang123/p/11618218.html

时间: 2024-10-09 07:43:19

构造函数语义学——Copy Constructor的建构操作的相关文章

C++对象模型——Copy Constructor 的建构操作(第二章)

2.2    Copy Constructor 的建构操作 有三种情况,会以一个object的内容作为另一个 class object的初值,最明显的一种情况就是对一个object做显式的初始化操作,例如: class X { ... }; X x; // 明确地以一个object的内容作为另一个class object的初值 X xx = x; 另两种情况是当object被当作参数交给某个函数时,例如 extern void foo(X x); void bar() { X xx; // 以x

C++对象模型——Default Constructor的建构操作(第二章)

第2章    构造函数语意学 (The Semantics of Constructor) 关于C++,最常听到的一个抱怨就是,编译器背着程序员做了太多事情.Conversion运算符就是最常被引用的一个例子. 2.1    Default Constructor的建构操作 C++ Annotated Reference Manual(ARM)指出"default constructors ...在需要的时候被编译器产生出来".关键字眼是"在需要的时候".被谁需要?

const成员-拷贝构造函数(copy constructor)

const成员 const成员:被const修饰的成员变量.非静态成员函数 必须类里面初始化 class Car { public: const int m_price = 0; //const常量设置 Car() :m_price(0) {} //也可以在构造函数中初始化 void run() const { //const一般写最后 cout << "run()" << endl; m_price = 10; } }; const成员函数 两个同名函数构成了

深入探索c++对象模型-&gt;2.2 Copy Constructor的构造操作

一.只有当class不展现出bitwise copy semanties时,编译器才会为class生成copy constructor.那么当什么时候回出现非bitwise copy呢? 1.当class内含一个member object而后者的class声明有一个copy constructor时(不论是显示声明或是被合成得到的): 2.当class继承自一个base class而后者存在一个copy constructor时. 3.当class声明了一个或多个virtual function

2.1 Default Constructor的建构操作

1.   存在错误的程序例子 class Foo { public:  int m_nVal;  Foo *m_pNext; }; void Foo_Bar( void ) {  Foo bar;    if ( bar.m_nVal || bar.m_pNext )  {   int i;   i++;  } } (1)正确的程序语意是要求 Foo 有一个 default constructor,可以将 members 初始化为0. (2)上述代码不会合成一个 default construc

关于 C++ 拷贝构造函数(copy constructor)中的形参必须为引用类型的详解

在<C++ primer>中文第四版中,关于拷贝构造函数(也称复制构造函数)是这样定义的:是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用. 问题来了!为什么形参必须为该类类型的引用?而不能是值传递方式?(PS:其实传值和传址都可以统一为传值,前者传的是对象的值,后者传的是对象的地址的值) 先看下边两组代码: 1. 1 class Example { 2 3 public: 4 5 Example() {} 6 7 Example(const Example&a

Copy constructor拷贝构造函数

翻译的是wikipedia关于copy constructor,地址:点击打开链接 拷贝构造函数是C++语言中用一个已有对象创建一个新对象的特殊构造函数,该函数的第一个参数必须是函数所在类型的引用(译注:const/non-const都可以,可以有多个参数剩余参数必须有默认值,一定要是引用,这些原因见后,问:有多个参数拷贝构造如何调用?). 通常编译器会自动为每一个class创建一个拷贝构造函数(显示拷贝构造);有些情况下程序员自定义了拷贝构造函数(用户自定义拷贝构造),这时编译器不合成拷贝构造

C++ Copy Constructor in depth (深入理解C++拷贝构造函数)

The copy constructor is a special kind of constructor which creates a new object which is a copy of an existing one, and does it efficiently. (拷贝构造函数是一种特别的构造函数,用于复制已经存在的对象到新生成的对象,这是一种高效的方式.) Here below is a simple declaration of a copy constructor: (

深度探索C++对象模型之第二章:构造函数语意学之Default constructor的构造操作

C++新手一般由两个常见的误解: 如果任何class没有定义默认构造函数(default constructor),编译器就会合成一个来. 编译器合成的的default constructor会显示的设定“class内每一个data member的默认值” 一.编译器在哪种情况下才会合成默认构造函数: 对于未声明构造函数的类,只有在以下四种情况下编译器才会为它们合成默认构造函数: 类的成员有一个类对象(Member Class Object),且该成员含有默认构造函数(default Const