5.4    对象的效率 (Object Efficiency)

在下面的效率測试中,对象构造和拷贝所须要的成本是以Point3d class声明为基准,从简单形式逐渐到复杂形式,包含Plain Ol' Data,抽象数据类型(ADT),单一继承,多重继承,虚拟继承,下面函数是測试的主角:

Point3d lots_of_copies(Point3d a, Point3d b) {
Point3d pC = a;
pC = b; // 1
b = a; // 2
return pC;
}

它内带四个memberwise初始化操作,包含两个參数,一个返回值以及一个局部对象pC,它也内带两个memberwise拷贝操作,各自是1和2的pC和b.main()函数例如以下:

main() {
Point3d pA(1.725, 0.875, 0.478);
Point3d pB(0.315, 0.317, 0.838);
Point3d pC;
for (int iters = 0; iters < 10000000; iters++)
pC = lots_of_copies(pA, pB);
return 0;
}

在最初两个程序中,数据类型是一个 struct 和一个拥有 public 数据的 class;

struct Point3d { float x, y, z; };
class Point3d { public: float x, y, z; };

对pA和pB的初始化操作是通过 explicit initialization list来进行的:

Point3d pA = {1.725, 0.875, 0.478};
Point3d pB = {0.315, 0.317, 0.838};

    这两个操作表现出bitwise copy语意,所以应该能够预期它们的运行有最好的效率.

    "memberwise"初始化操作和拷贝操作(Initialization and Copy): Public Data Members和Bitwise Copy Semantics.

    CC的效率比較好,是由于NCC循环中产生了六个额外的汇编语言指令.这个额外负担并不会反映出不论什么特定的C++语意,这两个编译器的"中间C输出"大致是相等的.

5.5    解构语意学 (Semantics of Destruction)

假设 class 未定义destructor,那么仅仅有在 class 内带的member object(或是 class 自己的base class)拥有destructor的情况下,编译器才会自己主动合成出一个.否则,destructor会被视为不须要,也就不需被合成(当然更不须要被调用).比如,Point,默认情况下就没有被编译器合成出一个destructor--尽管它拥有一个 virtual function:

class Point {
public:
Point(float x = 0.0, float y = 0.0);
Point(const Point &);
virtual float z();
private:
float _x, _y;
};

类似的道理,假设把两个Point对象组合成一个Line class:

class Line {
public:
Line(const Point &, const Point &);
virtual draw();
protected:
Point _begin, _end;
};

Line也不会拥有一个合成出来的destructor,由于Point并没有destructor.

    当从Point派生出Point3d(即便是一种虚拟派生关联)时,假设没有声明一个destructor,编译器就没有必要合成一个destructor.

    不论Point或Point3d,都不须要destructor,为它们提供一个destructor反而不符合效率.应该拒绝那种"对称策略"的想法:"已经定义了一个constructor,所以感觉必须提供一个destructor".其实,应该"须要"而非"感觉"来提供destructor,更不要由于不确定是否须要一个destructor,于是就提供它.

    为了决定 class 是否须要一个程序层面的destructor(或是constructor),请想想一个
class object的生命在哪里结束(開始)?

须要什么操作才干保证对象的完整?

这是敲代码时比較须要了解的.这也是constructor和destructor什么时候起作用的关键.比如,已知:

{
Point pt;
Point *p = new Point3d;
foo(&pt, p);
delete p;
}

能够看到,pt和p在作为foo()函数的參数之前,都必须先初始化为某些坐标值.这时候须要一个constructor,否则使用者必须明白地提供坐标值.一般而言,class 的使用者没办法检验一个local变量或heap变量以知道它们是否被初始化.把constructor想象为程序的一个额外负担是错误的,由于它们的工作有其必要性.假设没有它们,抽象化的使用就会有错误的倾向.

    当明白地 delete 掉p时会怎样呢?在不论什么程序上必须处理的吗?

是否须要在 delete 之前这么做:

p->x(0);
p->y(0);

不,当然不须要.没有不论什么理由说明在 delete 一个对象之前先得将其内容清除干净.也不须要归还不论什么资源.在结束pt和p的生命之前,没有不论什么"class使用者层面"的程序操作是绝对必要的,因此,也就不一定须要一个destructor.

    然而考虑Vertex class,它维护了一个由紧邻的"顶点"所形成的链表,而且当一个顶点的生命结束时,在链表上来回移动以完毕删除操作.假设这正是程序猿所须要的,那这就是Vertex destructor的工作.

    当从Point3d和Vertex派生出Vertex3d时,假设不供应一个 explicit Vertex3d destructor,那么还是希望Vertex destructor被调用,以结束一个Vertex3d object.因此,编译器必须合成一个Vertex3d destructor,其唯一的任务就是调用Vertex destructor.假设提供一个Vertex3d destructor,编译器会扩展它,使它调用Vertex destructor.一个由程序猿定义的destructor被扩展的方式类似constructors被扩展的方式,但顺序相反:

    1.假设object内带一个vptr,那么首先重设(reset)相关的 virtual table.

    2.destructor的函数本身如今被运行,也就是说vptr会在程序猿的码运行前被重设(reset).

    3.假设 class 拥有member class objects,而后者拥有destructors,那么它们会以声明顺序的相反顺序被调用.

    4.假设有不论什么直接的(上一层)nonvirtual base classes拥有destructor,它们会以其声明顺序的相反顺序被调用.

    5.假设有不论什么的 virtual base classes拥有destructor,而当前讨论的这个 class 是最尾端(most-derived)的 class,那么它们会以其原来的构造顺序的相反顺序被调用
.

    (这个顺序似乎有问题,应该为2,3,1,4,5.当中1在2,3之后)

    就像constructor一样,眼下对于destructor的一种最佳实现策略就是维护两份destructor实体:

    1.一个complete object实体,总是设定好vptr,并调用 virtual base class destructors.

    2.一个base class subobject实体:除非在destructor函数中调用一个 virtual function,否则它绝不会调用 virtual base class destructors并设定vptr.


    一个object的生命结束于其destructor開始运行之时,因为每个base class destructor都轮番被调用,所以derived class 实际上变成了一个完整的object.比如一个PVertex对象归还其内存空间之前,会依次变成一个Vertex3d对象,一个Vertex对象,一个Point3d对象,最后成为一个Point对象.当在destructor中调用member
functions时,对象的蜕变会由于vptr的又一次设定(在每个destructor中,在程序猿所提供的代码之前)而受到影响.在程序中实施destructor的真正语言将在第6章详述.

C++对象模型——解构语意学(第五章)的更多相关文章

  1. 【C++对象模型】第五章 构造、解构、拷贝 语意学

    1.构造语义学 C++的构造函数可能内带大量的隐藏码,因为编译器会扩充每一个构造函数,扩充程度视 class 的继承体系而定.一般而言编译器所做的扩充操作大约如下: 所有虚基类成员构造函数必须被调用, ...

  2. 《Android群英传》读书笔记 (2) 第三章 控件架构与自定义控件详解 + 第四章 ListView使用技巧 + 第五章 Scroll分析

    第三章 Android控件架构与自定义控件详解 1.Android控件架构下图是UI界面架构图,每个Activity都有一个Window对象,通常是由PhoneWindow类来实现的.PhoneWin ...

  3. “全栈2019”Java多线程第二十五章:生产者与消费者线程详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  4. “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. “全栈2019”Java异常第十五章:异常链详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  6. “全栈2019”Java第一百零五章:匿名内部类覆盖作用域成员详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. “全栈2019”Java第六十五章:接口与默认方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. C++对象模型——&quot;无继承&quot;情况下的对象构造(第五章)

    5.1 "无继承"情况下的对象构造 考虑以下这个程序片段: 1 Point global; 2 3 Point foobar() 4 { 5 Point local; 6 Poin ...

  9. ES6标准入门 第三章:变量的解构赋值

    解构赋值:从数组和对象中提取值,对变量进行赋值. 本质上,这种写法属于“匹配模式”:只要等号两边的模式相同,左边的变量就会被赋予对应的值. 1.数组的结解构赋值 基本用法 let [foo, [[ba ...

随机推荐

  1. Navicat Premium 12试用期的破解方法

    参考:https://blog.csdn.net/Jason_Julie/article/details/82864187 已测可用

  2. 洛谷P1035 级数求和

    #include <iostream> using namespace std; int main(){ long k,i; cin >> k; double s=0.0; ; ...

  3. MySQL字符集设定与查询

    一.字符集设定 MySQL数据库允许对服务器.数据库.数据表.数据列级别的字符集作出互不影响的设定. 1.对服务器字符集设定是通过配置文件中选项character-set-server 设置,如 ch ...

  4. 快速安装zabbix

    环境:CentOS 7.x 数据库mysql已事先安装 1.配置epel源 wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/r ...

  5. svn搭建脚本

    1.yum install subversion 2.输入rpm -ql subversion查看安装位置 我们知道svn在bin目录下生成了几个二进制文件. 输入 svn --help可以查看svn ...

  6. activemq常用配置

    所用版本为apache-activemq-5.15.4的版本 修改端口号 当端口号冲突时,可以修改这两个端口号.修改activemq.xml 修改里面的61616端口.修改jetty.xml,修改里面 ...

  7. Counting Kangaroos is Fun 求最少可见袋鼠数

    Description There are n kangaroos with pockets. Each kangaroo has a size (integer number). A kangaro ...

  8. [BZOJ 3823]定情信物

    题面 定情信物 题解 这题主要考高中物理和数学. 首先定义 \(f[i][j]\) 表示 \(i\) 维超立方体中第 \(j\) 维元素的数量,根据实际意义,我们可以推出递推式: \(f[i][j]= ...

  9. NYOJ-481平衡字符串

    平衡字符串 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描述 给你一定长度的字符串.字符串中只包含26个小写字母,首先我们把字母a-z分为2堆(a--m)和(n--z),判 ...

  10. SPOJ LCS2 多个串的最长公共子串

    这里串最多有10个,找所有串的最长公共子串 这里后缀自动机做,以第一个串建立后缀自动机,后面的串一个个去匹配,每次得到当前串在可到达状态上所能得到的最长后缀长度 拿所有串匹配后得到的结果进行计算 #i ...