深度探索C++对象模型——学习笔记1

第一章 关于对象

C++对象模型

1.简单对象模型

一个object是一系列的slot,每个slot对应一个成员。在这个模型中,把“指向成员的指针”放在object中。这样就避免不同成员类型不同,占用的不同的存储空间。

object大小=指针大小*成员个数

意义:slot被应用于C++指向成员指针观念中

2.表格驱动对象模型

这种模型讲所有的成员相关信息抽取出来,放在一个数据成员表中和一个成员函数表中,object中只有两个表格指针。

意义:成员函数表应用在支持虚函数方案中

3.C++对象模型

非静态成员变量被配置在每个class object中,静态数据成员存放在个别的class object之外,不管静态还是非静态成员函数都是放置在class object之外的。

虚函数:

每一个类都产生许多指向虚函数的指针,放在一个表格中,这个表格就是虚函数表(vtbl)

每个class object有一个指针指向虚函数表,这个指针被称为(vptr)。这个指针的设定与重置由class的构造函数、析构函数,copy assignment自动完成。每个类关联的对象类型信息(RTTI)由虚函数表指出来

这种模型的优点:空间和存取时间的效率方面不错,主要缺点就是程序代码为改变,而类对象的非静态数据成员发生改变,代码就得重新编译。这一个缺点前面的表格驱动模型就不存在了,但是也增加了一层间接性,效率方面就会大打折扣了。

1.2关键词所带来的差异

1.3对象的差异

一个class object内存大小
非静态数据成员总和大小(要考虑到数据对齐占用的空间)
支持虚函数额外的负担

指针类型会交到编译器如何解释某个特定地址中的内存内容及其大小

一个指针或一个引用之所以支持多态,因为他们并不引发内存中的任何“与类型有关的内存委托操作”,会受到改变,只是他们所指向的内存的“大小和内容解释方式”而已

第二章 构造函数语意学

2.1 默认构造器的构造操作
默认构造函数在需要的时候被编译器产生出来
对于一个类没有声明的构造函数,那么会有一个默认的构造函数被隐式声明出来。隐式声明出来的构造函数也是一个没有啥用的构造函数

带有默认构造函数的member class object

编译器如何避免合成多个默认构造函数
把合成的默认构造函数,析构函数,拷贝构造函数,复制拷贝操作以inline方式完成。如果函数太复杂,会合成一个explicit non-inline static实例

在这种情况下,默认构造函数对这个member class object进行初始化(只满足编译器的需要),而对于其他的数据成员初始化则是程序员的责任。要清楚被合成的默认构造函数知识满足编译器的需要,而不是程序的需要。

如果设计者提供了默认构造函数,编译器则会扩张现有的默认构造函数

带有默认构造函数的父类

子类的默认构造函数被认为是nontrivial的,因此需要被合成。调用base class 的默认构造函数。如果设计者提供了多个构造函数,而没有默认的构造函数,编译器会扩展现有的每一个构造函数,调用所有有必要的默认构造函数

带有虚函数的类

(1)class声明或者继承了一个虚函数
(2)class派生自一个继承串链,其中有一个或多个虚基类

扩张行为:虚函数表,虚函数指针

带有一个虚基类的类
编译器会安插那些允许虚基类的执行期存取操作的代码

不在以上4中情况又没有声明构造函数的类,他们拥有的是隐式平凡的默认构造函数,实际上并不会被合成

2.2copy constructor 的构造操作
有三种情况,一个对象的内容作为另一个对象的初值

  • 对一个对象做显式初始化。 X a=x;
  • 对象作为函数参数
  • 对象作为函数返回值

default memberwise initialization
没有显示拷贝构造函数,每一个数据成员从一个object拷贝到另一个object,但是并不会拷贝member class object,以递归的方式实行 memberwise initialization

bitwise copy semantics(位逐次拷贝)

不要bitwise copy semantics

不展现bitwise copy semantics的情况
(1)class 内含member object ,且后者class声明了一个copy constructor(不管是显示声明或者被编译器合成)
(2)class继承一个base class后者存在一个copy constructor
(3) class声明了一个或者多个虚函数
(4)class派生自一个继承串链,其中有一个或多个虚基类

重新设定虚函数表的指针
编译器需要合成一个copy constructor将vptr适当的初始化
派生类对象对派生类对象初始化没有问题(bitwise copy)
派生类对象对基类对象初始化存在问题(bitwise copy)基类对象使用派生类对象的虚函数表。

处理virtual base class subobject

2.3程序转化语意学

显示初始化操作

void foo_bar()
{
    X x1(x0);
    X x2(x0);
}

程序转化过程:
重写每一个定义,剥夺初始化操作(定义是指占用内存的行为)
class中的copy constructor调用操作会被安插进去

参数的初始化

void foo(X xx);
X _tmp0;
_tmp0.X::X(xx);
foo(tmp0);

当一个class object当成一个参数传递给一个函数,或者作为一个函数的返回值
编译器导入临时性object,调用copy constructor进行初始化,将这个临时性的object交给函数,函数结束后,析构掉这个临时性object。

返回值的初始化

X bar()
{
    ...
    return xx;
}

从局部对象拷贝出来,有一个双阶段转化
1,加上一个额外参数,class reference,这个参数用于放置返回值
2.在return指令之前安插一个copy constructor,将想要传回的值内容当做上面新增加参数的初值

X xx=bar();

void bar(X &_result)
{
    X xx;
    xx.X::X();
    ....
    _result.X::XX(xx);
    return ;
}

在编译层面做优化
name return value 优化

copy constructor 要还是不要
当一个函数以传值的方式传回一个class object,这个class有一个copy constructor,会导致程序转化。编译器将copy constructor的调用操作优化。

2.4 成员们的初始化队伍
这一小节主要讲解成员初始化列表的意义

必须使用成员初始化列表:
(1)初始化一个引用成员
(2)初始化一个常量成员
(3)调用基类的constructor,而它拥有一组参数
(4)调用一个成员类的constructor,她拥有一组参数

当一个class members object初始化在构造函数类中,会先产生一个临时性object,然后初始化它,再用一个赋值运算给这个类中的 class members object,在析构临时性的object。而采用成员初始化列表效率会高一些

class word{
    String _name;
    int _cnt;
    word()
    {
        _name=0;
        _cnt=0;
    }
}

word::word()
{
    _name.String::String();
    String tmp=String(0);
    _name.String::operator=(tmp);
    tmp.String::~String();
    _cnt=0;
}

由于这个原因,程序员要坚持所有的成员初始化操作在成员初始化列表中完成。

list项目的顺序是由class中的members声明顺序决定的,不是由list上的排列顺序决定的

编译器堆初始化列表意义处理并可能重新排序,他会安插一些代码到constructor中,并置于explicit user code之前

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值