深度探索C++对象模型

深度探索C++对象模型

第一章 关于对象

1.1 C++对象模型

Nonstatic data members被配置于每一个class object内

static data members 被存放在所有的class object之外

static 和 nonstatic function members 也被存放在所有的class oject之外

Virtual functions 分为以下两步:

  1. 每个class产生一堆指向virtual functions的指针,放在表格中。这个表格被称为virtual table(vtbl);
  2. 每一个class object被添加一个指针,指向virtual table。这个指针称为vptr。(vptr的设定和重置由每个class的constructor、destructor和copy assignment运算符完成)每个class所关联的 type_info object也经由virtual table被指出,通常放在表格的第一个slot处。

深度探索C++对象模型_第1张图片

加上继承

在之前的基础上再加上一个base class table,在slot中多一个指向此表的相关地址。

虚拟继承(virtual)例如:

class istream:virtual public ios{};
class ostream:virtual public ios{};
class iostream:public istream,public ostream {};

不管在继承串链中派生多少次,永远只有一个实例//

1.2 关键词带来的差异
1.3 对象的差异
多态:

C++中,多态只存在于一个个的public class体系中。

C++支持多态的方式:

1、经由一组隐含的转化操作。例如把一个derived class指针转化为一个指向其public base type的指针:

shape * ps=new circle();
//ps保留了基类的部分

2、经由virtual functions机制:

ps->rotate();

3、经由dynamic_cast和typeid运算符:

if(circle * pc=dynamic<circle*>(ps))....

​ 多态的主要用途是经由一个同用的接口来影响类型的封装,这个接口通常定义在base class中。

指针的类型

“指针类型”会教导编译器如何解释某个特定地址中内存大小。这也是为什么一个类型为void*的指针只能够持有一个地址,而不能通过它操作所指向的object的缘故。

加上多态之后

当一个base class object被直接初始化为一个derived class object时,derived object就会被切割以塞入较小的base type内存中,derived type没有留下任何痕迹。

C++通过class的指针和引用来支持多态。

补充:
assert(****)  //判断内部是否有错,(内部为假)终止程序
malloc()//一个分配动态内存大小的函数,返回void*
strlen() //返回长度,不包含最后的NULL
static_cast<type> (变量) //强行转换不安全(子->父安全)
dynamic_cast<type> (变量)  //和上面比更加安全

第二章 构造函数语意学

2.1 Default Constructor的构造操作

4种有效的默认构造:

1、带有Default Constructor的Member Class Object

2、带有Default Constructor的Base Class

3、带有一个Virtual Function的Class

4、带有一个Virtual Base Class 的Class

2.2 Copy Constructor的构造操作

三种使用情况:显式等于,函数参数,函数返回

不会有逐位拷贝的情况:

1、当class内含一个member object而后者的class声明一个copy constructor。

2、当class继承一个含有copy constructor的class

3、当class派生自一个继承串连,其中有一个或者多个virtual base class

4、当class声明一个或者多个virtual function。

2.3 程序转化语意学
X foo() 
{ 
    X xx; 
    if(...) 
        return xx; 
    else 
        return xx; 
}

形如foo(),所有return 指令传回相同的对象,编译器会自动对其做NRV优化,方法是以一个参数result取代返回对象:

void  foo(X &result)
{
    result.X::X();
    if(...)
    {
        //直接处理result
        return;
    }
    else
    {
        //直接处理result
        return;
    }
}

对于一句类似于X a = foo()这样的代码,NRV优化后的代码相较于原代码节省了一个临时对象的空间(省略了xx),同时减少了两次函数调用(减少xx对象的默认构造函数和析构函数,以及一次拷贝构造函数的调用,增加了一次对a的默认构造函数的调用)。

2.4 成员函数初始化队列

在构造函数中对于对象成员的初始化发生在初始化队列中——或者我们可以把初始化队列直接看做是对成员的定义,而构造函数体中进行的则是赋值操作。
初始化队列运行在前(初始化顺序按class中成员声明顺序,而不是初始化队列顺序),构造函数体在后。
编译器实际上会一一操作初始化队列,按顺序在构造函数体内安插初始化操作,并在原来的显式代码之前。

Data语意学

3.1 Data Member的绑定、

一个inline成员函数躯体之内的一个data member 绑定操作,会在整个class声明完成后才发生。

但是,对于成员函数的参数列表并不是,仍然需要采取保护措施(把“nested type声明”放在class的起始处)

3.2 Data Member的布局
3.3 Data Member的存取

static data member的存取地址是独立出来的指针,而不是对应类成员的指针。

nonstatic data member的存取,利用类的指针位置+offset(偏移量)(这里的偏移量会主动加1,为了区分“一个指向data member的指针,用以指出class 的第一个member"和”一个指向data member的指针,没有指出任何member"两种情况)。

3.4 “继承”和Data Member
只要继承不要多态

易犯错误:

1、会重复相同的操作

2、把一个class分解为两次或者多层,有可能会膨胀需要的空间。(见p105示例)

虚拟继承

多态、继承等

3.5 对象成员的效率
3.6 指向Data Member的指针

通过地址访问类成员是,其实存在一步内部转化:

func(bmp+sizeof(Base1),pd)   //bmp为地址
//防止bmp为0
func(bmp?bmp+sizeof(Base1):0,pd)

Function语意学

4.1 Member的各种调用方式
Nonstatic Member Functions

通过this来调用

Virtual Member Functions

this加上vptr[index]调用

Static Member Functions

没有this指针

4.2 Virtual Member Functions(虚拟成员函数)

单一继承下的多态
深度探索C++对象模型_第2张图片

多重继承下的Virtual Functions

深度探索C++对象模型_第3张图片

虚拟继承下的Virtual Functions

深度探索C++对象模型_第4张图片

4.3 函数的效能

nonmember function\nonstatic function\static function的效率相同

继承深度加深,增加的成本来自constructor

virtual Functions的调用:继承深度加深,增加的成本来自vptr的设定

4.4 指向Member Function的指针
支持“指向Virtual Member Functions”的指针

虚拟机制也可以使用“指向 Member Functions”的指针,只是返回的索引值;

而Member Functions返回的是地址。

在多态继承下,指向Member Functions的指针
4.5 Inline Functions
形式参数

面对带来“副作用的实际参数”,需要引入临时对象。

其他的常量表达式先求值再替换

其余的直接代换

局部变量

需要生成临时对象

构造、析构、拷贝语意学

​ 当一个class中有纯虚函数,则此类无法拥有实例。对于其中的data member必须有显示的构造函数赋值。

​ class的data member应该被初始化,只在constructor或者其他的member functions中指定初值。

纯虚函数的存在

只能被静态调用

虚拟规格的存在

不一定都需要把class中的函数声明为虚拟函数,视函数体中的需要而定

虚拟规格中const的存在

const使用看函数的内容是否涉及到需要修改data member

重新考虑class的声明
5.1 “无继承”情况下的对象构造
抽象数据类型(ADT)
为继承做准备
5.2 继承体系下的对象构造
虚拟继承

只有一个完整的class object被定义出来,它才能被调用。如果object只是某个完整object的subobject,它就不能被调用。

vptr初始化语意学

在一个class的constructor中,经构造出来的对象来调用一个virtual function,其函数实例应该在此class内。

vptr的初始化在base class constructor调用操作后,在程序员提供的代码或者是“member initialization list”所列的members初始化之前。

vptr必须被设定的两种情况:
1、当一个完整的对象被构建出来。例如Point a;其中的constructor必须设定vptr

2、当一个subobject constructor调用一个virtual function。

5.3 对象复制语意学

一个class对于默认的copy assignmemt operator,在下面的情况不会表现bitwise copy语义:

1、当class内含一个member object而后者的class声明一个copy assignment operator。

2、当class继承一个含有copy assignment operator的class

3、当class派生自一个继承串连,其中有一个或者多个virtual base class

4、当class声明一个或者多个virtual function。

总结:尽量不要允许一个virtual base class 的拷贝操作。甚至不要在任何virtual base class中声明数据。

5.4 对象的效能
5.5 析构语意学

只有在class内含的member object(或者是class自己的base class中)拥有destructor,才会自动合成一个destructor。

destructor的扩展顺序:p235

执行期语意学

6.1 对象的构造和析构

尽量把object的声明放在使用它的程序段的附近

全局对象

在程序启动的时候,才会调用所包含的constructor,因此需要静态初始化

p244图

建议:不要使用那些需要静态初始化的global objects。

局部静态变量

编译器内部使静态变量只在调用(需要)的时候被构造出来。

数组对象
Default Constructor和数组
6.2 new和delete运算符
针对数组的new的语意
Placement Operator new的语意
Point2w *ptw=new (area) Point2w

area指向内存的一块区域,用来放置新产生的Point2w对象

6.3 临时对象
临时性对象的迷思

你可能感兴趣的:(笔记)