深度探索C++对象模型之第二章:构造函数语意学之Copy constructor的构造操作
C++ Standard将copy constructor分为trivial 和nontrivial两种:只有nontrivial的实例才会被合成于程序之中。决定一个copy constructor是否是nontrivial的,则是由classs是否具有 bitwise copy semantics,在以下四种情况下:class 不具有bitwise copy semantics,如果一个已经声明的类缺乏copy constructor ,编译器为了正确处理“以一个 class object 作为另一个class object的初值”,编译器会为class合成一个copy constructor。
- 当class内含一个member object,而这个member object的class声明中有一个copy constructor(不论是用户explicitly define还是编译器合成的)
- 当class继承自一个base class,而这个bass class 存在 copy constructor(无论是用户explicitly define or 编译器合成的)
- 当classs声明了一个或多个virtual function
- 当类派生自一个继承串链,其中有一个或多个 virtual base class时
对于以上四种情况,类不再具有bitwise copy semantics(逐位次拷贝语义学)时,而且默认构造函数若未被声明的话,default constructors会被视为nontrival.
一、下面来介绍下什么叫bitwise copy semantics:
给两个例子:大家自己对比一下吧
比如下面这个例子就展示了 bitwise copy semantics:
class Word{
public:
Word (const char*) ;
~Word() {delete [] str;}
private:
int cnt;
char *str;
};
而下面这个例子Word类就不再展现出bitwise copy semantics,编译器必须合成出一个copy constructors,以便调用member class String object的copy constructor.
class Word{
public:
Word(const String&);
~Word();
private:
int cnt;
String str;
};
//其中String声明了一个explicit copy construcotr
class String{
public:
String(const char*); //显示声明的默认构造函数
String(const char &);//explicit copy constructors
~String();
};
则针对上述情况,编译器为类Word合成一个copy consructor,如下所示:注意正如我们期待的一样:在被合成的copy constructor中,如整数、指针、数组等nonclass members也会被复制。
//C++伪代码
inline Word::Word(const Word& wd)
{
str.String::String(wd.str);
cnt = wd.cnt;
}
二、下面介绍三种会以一个object的内容作为另一个class object初值的情况:
第一种情况就是对一个object做显示的初始化操作:
class X{...};
X x;
//显示的用一个object的内容作为另一个class object的初值
X xx = x;
第二种情况就是当object被当做某个参数传递给某个函数时;
extern void foo(X x);
void bar()
{
X xx;
//以xx作为foo()第一个参数的初值(隐式的初始化操作)
foo(xx);
}
第三种情况就是当函数传回一个class object时:
X foo_bar()
{
X xx;
return xx;
}
当class object以相同class的另一个object作为初值时,其内部是以所谓的default memberwise initialization(逐成员初始化)完成的:即把每一个内建或派生的data member的值,从一个object拷贝到另一个object上,不过他并不会拷贝其中的class object,而是递归的实行member initializaiton.
三、下面我们讨论四种情况的第三种:当classs声明了一个或多个virtual function
当class声明了一个或多个virtual function时,编译器会发生以下扩张行为:
- 增加一个virtual function table(vtbl),内含每个有作用的virtual function地址
- class objet增加一个指向vtbl的指针(vptr).
当编译器导入一个vptr(是新导入一个vptr,例如将base class object以derived class的object内容初始化时,重要!!!)时,class就不再展现bitwise copy semantics啦,所以编译器需要合成一个copy constructor,来让vptr正确的初始化:
来看一个例子:
class ZooAnimal{
public:
ZooAnimal();
virtual ~ZooAnimal();
virtual void animate();
virtua void draw();
private:
//ZooAnimal的内建数据...
};
class Bear : public ZooAnimal{
Bear();
void animate(); //virtual function
void draw() ; //虚函数
virtual void dance();
private:
//Bear的内建函数
};
当以一个ZooAnimal object作为另一个ZooAnimal object的初值 或 以一个Bear object作为另一个Bear object的初值时,此时并不是新导入一个vptr,virtual function table仍然是原有的表,所以此时具有bitewise semantics,此时编译器要产生的拷贝构造函数是trivial(无意义的),根本不会合成。
举个例子如下:
Bear yogi;
Bear whii = yogi;
经过上述操作之后yogi oobject和whii object中分别含有的vptr指向的vtbl是相同的,如下图所示:

而如果一个base class object以一个derived class object的初值初始化时,则此时base class 就不再具有bitwise semantics
举个例子:
Bear yogi;
ZooAnimal whii = yogi;
此时编译器需要合成一个nontrivial的copy structor来显示设定base object的vptr指向base class的vptr,而不是直接从右边derived class object中将vptr的值拷贝出来。 如下图所示:

四、下面我们讨论四种情况的第三种:处理virtual Base class实例
bitwise copy semantics失效发生在一个class object以其derived object作为初值时,而不是发生在一个class object 以一个同类的object作为初值时,(后者使用bitwise copy拷贝绰绰有余)
举个例子如下:
//定义一个浣熊类
class Raccoon : public virtual ZooAnimal {
public:
Raccoon() { }
Raccoon(int val) { }
private:
//Raccon的内建数据 }; //定义一个大熊猫类 继承自浣熊
class RedPanda : public Raccoon{
public:
RedPanda() { }
RedPanda(int val) { }
private:
//RedPand的内建数据
};
记住浣熊才是这个具有虚基类的类,需要编译器合成trivial的 copy constructor就发生在当 浣熊以大熊猫作为初值时。因为每个编译器对于虚拟继承的承诺,都代表必须让 “derived class object中的virtual base class subobject位置”在执行期就准备妥当,即如下情况:
RedPanda little_red;
Raccoon little_critter = little_red;
在这种情况下,编译器必须合成一个copy constructor,安插一些代码以设定virtual base class的初值(或只简单的确定它没有被抹除),对每一个members执行必要的memberwise初始化操纵,以及执行其他的内存相关操作。

好啦,本节关于何时编译器会合成一个nontrivial的copy constructor的讨论就结束啦,大家要记得复习哦。
深度探索C++对象模型之第二章:构造函数语意学之Copy constructor的构造操作的更多相关文章
- 《深度探索C++对象模型》第二章 | 构造函数语意学
默认构造函数的构建操作 默认构造函数在需要的时候被编译器合成出来.这里"在需要的时候"指的是编译器需要的时候. 带有默认构造函数的成员对象 如果一个类没有任何构造函数,但是它包含一 ...
- 【C++对象模型】第二章 构造函数语意学
1.Default Constructor 当编译器需要的时候,default constructor会被合成出来,只执行编译器所需要的任务(将members适当初始化). 1.1 带有 Defau ...
- 深度探索C++对象模型之第二章:构造函数语意学之Default constructor的构造操作
C++新手一般由两个常见的误解: 如果任何class没有定义默认构造函数(default constructor),编译器就会合成一个来. 编译器合成的的default constructor会显示的 ...
- 深度探索C++对象模型之第二章:构造函数语意学之成员初始值列表
当我们需要设置class member的初值时,要么是经过member initialization list ,要么在construcotr内. 一.先讨论必须使用member initializa ...
- 《深度探索C++对象模型》第一章 | 关于对象
C++对象模式 非静态数据成员放置在每个类对象内,静态数据成员则被放置在所有类对象之外.静态和非静态的成员函数也被放置在所有类对象之外.每个类产生一堆指向虚函数的指针,放在虚表(vtbl)中.每个类对 ...
- 深度探索C++对象模型之第一章:关于对象之C++对象模型
一.C和C++对比: C语言的Point3d: 数据成员定义在结构体之内,存在一组各个以功能为导向的函数中,共同处理外部的数据. typedef struct point3d { float x; f ...
- 深度探索C++对象模型之第一章:关于对象之对象的差异
一.三种程序设计范式: C++程序设计模型支持三种程序设计范式(programming paradiams). 程序模型(procedural model) char boy[] = "cc ...
- 深度探索C++对象模型第四章:函数语义学
C++有三种类型的成员函数:static/nonstatic/virtual 一.成员的各种调用方式 C with class 只支持非静态成员函数(Nonstatic member function ...
- 深度探索C++对象模型之第一章:关于对象之关键词所引起的差异
————如果不是为了努力维护与C之间的兼容性,C++远比现在简单的多. 如果一个程序员渴望学习C++,但是他却发现书中没有熟悉的struct,一定会苦恼,将这个主题包含到C++里,可以提供语言转移时的 ...
随机推荐
- node 创建静态web服务器(上)
声明:本文仅用来做学习记录. 本文将使用node创建一个简单的静态web服务器. 准备工作: 首先,准备好一个类似图片中这样的页面 第一步: 创建 http 服务: const http = requ ...
- 前端(十五)—— JavaScript事件:绑定事件方式、事件的冒泡和默认事件、鼠标事件、键盘事件、表单 事件、文档事件、图片事件、页面事件
JS事件:绑定事件方式.事件的冒泡和默认事件.鼠标事件.键盘事件.表单 事件.文档事件.图片事件.页面事件 一.事件的两种绑定方式 1.on事件绑定方式 document.onclick = func ...
- C#常用设计模式
1.单例模式 单例模式恐怕是很多开发人员最先接触到的模式之一,可以认为就是一个全局变量.它的初始化过程无非就是一开始就new 一个instance,或者惰性初始化等需要用到的时候new 一个insta ...
- Python加密模块
RSA加密 # 生成公钥私钥对象 import rsa pub_key_obj, priv_key_obj = rsa.newkeys(1024) ''' 这里的1024是二进制位数, 也就是说他加密 ...
- vue在v-for循环中绑定v-model
原始示例 <div v-for="item in items"> <input type="text" v-model="'good ...
- 云栖专辑|阿里开发者们的第二个感悟:PG大V德哥的使命感与开放心态
摘要: 2018年12月20日,云栖社区3岁.阿里巴巴常说“晴天修屋顶”,所以我们特别制作了这个专辑——分享给开发者们20个阿里故事,50本书籍. 2015年12月20日,云栖社区上线.2018年12 ...
- css制作旋转风车(transform 篇)
做这个案例之前首先要大概了解CSS的transform的属性 transform 属性向元素应用 2D 或 3D 转换.该属性允许我们对元素进行旋转.缩放.移动或倾斜. 看看效果图 打开的时候自动旋转 ...
- mui与springMVC前后端分离
这个小dome简单来说的前后端分离,通过跨域调用接口来显示数据. 前端用到mui框架,主要来显示数据. 后端用到Java的springMVC,restful服务来做增删改查管理, 这里主要实现动态显示 ...
- eclipse背景设置什么颜色缓解眼睛疲劳
eclipse背景设置什么颜色缓解眼睛疲劳 1.打开window->Preference,弹出Preference面板 2.展开General标签,选中Editors选项,展开. 3.选中 Te ...
- TopCoder[SRM587 DIV 1]:ThreeColorability(900)
Problem Statement There is a H times W rectangle divided into unit cells. The rows of cells are ...