Inside the C++ Object Model 深度探索对象模型 1-Object, 2-Constructor

来源:互联网 发布:java for循环语句 编辑:程序博客网 时间:2024/04/27 15:08

前言摘录 >BarbaraMoo常说 管理一个团队 就像放牧一群骄傲的猫.

1 关于对象Object

>参数化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class type, int dim>          
class Point          
{          
public:          
    Point(type coords[dim])           
    {          
        for (int index = 0; index < dim; index++)          
                mCoords[index] = coords[index];          
    };          
    type& operator[](int index)           
    {          
        assert(index < dim && index >= 0);          
        return mCoords[index];          
    }                  
private:          
    type mCoords[];          
};

加上封装后的布局成本 Layout costs for adding Encapsulation

>C++在布局和存储时间上主要的额外负担是由Virtual引起 
-virtual function机制 支持一个有效率的"执行期绑定" runtime binding; -virtual base class 实现"多次出现在继承体系中的base class, 有一个单一而被共享的实体" [棱形结构] -一些多重继承下的额外负担

1.1 C++对象模式

>class data: static/nonstatic; class member functions: static/nonstatic/virtual;

简单对象模型

>一个class的大小, 指针大小乘以class中所声明的members的数目;

表格驱动对象模型

>Member Data Table+Member Function Table

C++对象模型

1)class产生一堆指向virtual functions的指针 放在virtual table里面; 
2) vptr 每一个class object被添加了一个指针 指向相关的vtable, vptr的设定和重置都由每一个class的constructor和destructor,copy assignment运算符完成; 每个class关联的type_info object(RTTI)由vtable指出.

加上继承 Inheritance
>单一继承 多重继承 虚拟/共享继承 -虚拟继承的情况下 base class不管被派生多少次, 只会存在一个实体subobject.

对象模型如何影响程序

>不同的对象模型会导致'现有代码修改', '加入新的程序代码'

1.2 关键词差异 keyword distinction

关键词的困扰

>class-struct ADT(abstract data type)

策略性正确的struct

>C struct在C++中的合理用途, 当你要传递一个复杂的class object的全部或者部分到某个C函数中去时, struct可以将数据封装起来, 保证拥有与C兼容的空间布局. 这项保证只在组合composition的情况下才存在, 对于继承, 编译器会决定是否应该有额外的data members被安插到base struct subobject中.

1.3 对象的差异

>C++直接支持3种程序设计典范 programming paradigms

1) 程序模型 procedural model 2) 抽象数据模型 abstract data type ADT model 3) 面向对象模型 object oriented model

>只有通过point和reference的间接处理才支持OO设计的多态性质.

>C++支持多态的方法

1)隐含的转化操作, 把derived class指针转化为指向public base type的指针 Ex. shape* ps = new circle();

2)virtual function机制 3) dynamic_cast和typeid

>一个class object的内存 -nonstatic data members总和; -应alignment的需求填补边界padding; -支持virtual由内部产生的额外负担 overhead;

指针的类型

>指针的内存大小是固定的, 与指向的数据类型无关;

1) 指向地址1000的整数指针 在32bit机器上 地址空间1000~1003, 4byte/一个word空间; 2) 如果String是8byte, 一个4byte的字符指针和一个表示字符串长度的int. 3) ZooAnimal指针为 1000~1015(int+string+vptr 4+8+4)

1
2
3
4
5
6
7
8
9
10
class ZooAnimal      
{      
public:      
    ZooAnimal();      
    virtual ~ZooAnimal();      
    virtual void rotate();      
protected:      
    int loc;      
    string name;      
};

加上多态

Ex. Bear b; ZooAnimal* pz = &b; Bear* pb = &b; // ((Bear*)pz)->BearMember(); pb->BearMember(); [dynamic_cast]

Ex. Bear b; ZooAnimal za = b; //切割 sliced

>当一个base class object被直接初始化为一个derived class object时, derived class object会被切割, 塞入较小的base type 内存中; 没有多态出现;

---Section1 End---

2 构造函数语意学 The Semantics of Constructors

2.1 Default Constructor的构建操作

>default constructor在需要的时候被编译器产生出来; 4种情况

带有Default Constructor的Member Class Object

>一个class没有constructor, 但它内含一个member obejct, 后者有default constructor, 那这个class的implicit default constructor是nontrivial, 编译器需要为class合成一个default constructor.

带有Default Constructor的Base Class

>一个没有constructor的class派生自一个带有default constructor的base class, 则这个derived class的default constructor会被视为nontrivial.

带有一个Virtual Function的Class

1) class声明/继承一个virtual function; 2) class派生自一个继承串链, 其中有virtual base classes.

>编译期间的扩张操作 1) virtual function table(vtbl)会被编译器产生出来, 内置class的virtual function地址. 2)在每个class object中, 额外的pointer member(vptr)会被编译器合成出来, 内含相关class vtbl地址.

带有一个Virtual Base Class的Class

>棱形继承/虚拟继承;  在derived class object的每一个virtual base class中安插一个指针; 经由reference或pointer来存取一个virtual base class的操作都可以通过相关指针完成.

总结 在合成的default constructor中只有base class subobjects和member class object会被初始化, 其他的nonstatic data member Ex. 整数 整数指针 整数数组等不会被初始化.

2.2 Copy Constructor的构建操作

>3种情况, .  Ex. class X {...}; X x;

1) 明确以以一个object的内容作为另一个class object的初值: X x2 = x; 2) 当object作为参数: foo(x2);  3) 当函数传回一个class object: X foo_2() { return x2;}

>拷贝构造函数的第一个参数类型是class type, 第二参数以及后继参数需要有默认值;

Default Memberwise Initialization

>DMI把每一个内建的或派生的data member(指针,数组,etc)的值, 从某个object拷贝一份到另一个object, 不会拷贝其中的member class object, 而是以递归的方式施行memberwise initializtion.(赋值初始化)

>default constrctor和copy constructor在必要的时候才由编译器产生出来 (当class不展现bitwise copy semantics时)

>一个class object可以从两种方式复制得到, 1)被初始化-copy constructor 2)被指定 assignment -copy assignment operator.

Bitwise Copy Semantics位逐次拷贝 - 不要Bitwise Copy Semantics

一个class不展现bitwise copy semantic的情况:

1) 当class内含member object, 后者的class声明有一个copy constructor时(不论是设计者明确声明或者编译器合成的);

2)当class继承自一个base class, 后者存在有一个copy constructor时.

3)当class声明了一个或多个virtual functions时.

4)当class派生自一个继承串链, 其中有一个或多个virtual base classes时.

>前2种情况, 编译器必须将member或base class 的copy constructor调用操作安插到被合成的copy constructor中.

重新设定Virtual Table的指针

>编译期间的2个程序扩张操作 -增加一个vtbl, 内含每一个有作用的virtual function的地址; -将一个指向vtbl的指针vptr安插在每一个class object内;

>当编译器导入一个vptr到class中时, 该class就不再展现bitwise semantics了;

处理Virtual Base Class Subobject

>一个class object如果以另一个object作为初值, 而后者有一个virtual base class subobject, 会使bitwise copy semantics失效;

2.3 程序转化语意学 Program Transformation Semantics

明确的初始化操作 Explicit Initialization

>程序转换2个阶段 1)重写每个定义(占用内存的行为), 初始化操作被剥除; 2) 安插class的copy constructor操作;

X x1(x0); -->编译器安插X copy constructor的操作 X x1; x1.X::X(x0);

参数的初始化 Argument Initialization

>把一个class object当作参数传给一个函数(或作为函数的返回值), 相当于 X xx = arg; 的操作;

Ex. void foo(X x0); X xx; foo(xx); 编译器的操作--> X __temp0; __temp0.X::X(xx); foo(__temp0); //形参需要改变为reference; void foo(X& x0); 另一种实现方法是以拷贝建构copy construct的方式把实际参数直接建构在其所在位置上.(Borland C++)

返回值的初始化 Return Value Initilization

>双阶段转化 1)加上一个额外参数, 类型是class object的reference. 这个参数用来放置被拷贝构建copy constructed而得的返回值; 2)在return指令之前安插一个copy constructor调用操作, 将想要传回的object的内容当作上述新增参数的初值;

Ex. X bar() { X xx; return xx;} 编译器转换--> void bar(X& __result) { X xx; xx.X::X(); __result.X::X(xx); return;}

函数指针 X (*pf)(); pf=bar; --> void (*pf)(X&); pf=bar;

在使用者层面做优化 Optimization at the User Level

>定义一个计算用的constructor; Ex. X bar(const T& y, const T &z) { return X(y, z); } --> void bar (X& __result, const T& y, const T& z) { __result.X::X(y, z); return; }

在编译器层面做优化 Optimization at the Compiler Level

>编译器优化操作 Named Return Value(NRV)优化;

>批评1)优化由编译器完成, 实现程度外部不可知; 2)函数变得比较复杂时, 优化难以施行; 3)程序员在某些情况下不希望应用程序优化;

Ex. X xx0(1024); --> xx0.X::X(1024); //单一的constructor操作; Ex. X xx1 = X(1024); X __temp; __temp.X::X(1024), xx1.X::X(__temp); __temp.X::~X(); //调用两个constructor, 对于临时object调用destructor;

Copy Constructor 要还是不要

>default copy constructor被视为trivial, bitwise copy不会导致memory leak和address aliasing, 快速安全;

>memcpy()或memset()都只有在classes不含任何由编译器产生的内部members时才能有效运行.

摘要 copy constructor的应用 会使编译器多少对程序代码做部分转化, 当函数以传值方式传回class object, 而该class有copy constructor(明确定义或者合成出来的), 会导致程序转化--在函数的定义或使用上; 编译器也将copy constructor的调用操作优化, 以一个额外的第一参数取代NRV.

2.4 成员们的初始化队伍 Member Initialization List

>必须使用member initialization list的情况

1)初始化一个reference member; 2)初始化一个const member; 3)调用一个base class的constructor, 而它拥有一组参数时; 4) 调用一个member class的constructor, 而它拥有一组参数时;

>member initialization list提高效率, 避免编译器内部扩张constructor; Ex.

1
2
3
4
5
6
7
8
9
class Word{   
public:   
    Word(){   
        mName = 0;   
        mCnt = 0; }   
private:   
    string mName;   
    int mCnt;   
};

扩张-->Word::Word() { mName.string::string(); string temp = string(0); mName.string::operator=(temp); temp.string::~string(); mCnt = 0; }

>提高效率的办法 Word::Word() : mName(0) { mCnt = 0;  }  扩张--> Word::Word() { mName.string::string(0); mCnt = 0; } //调用了string(0)的constructor;

Notice: >list中的初始化顺序是由class中的members的声明次序决定的, 不是initialization list的排列次序决定的. >Initialization list的操作被放在explicit user code之前;

>编译器会对initialization list处理并可能重新排序, 会安插一些代码到constructor内, 放置于explicit user code之前.

---Section2 End---