深度探索C++对象模型》读书笔记(1)

***C++对象模型***
在C++中,有两种class data members:static和nonstatic,以及三种class member functions:static、nonstatic和virtual。已知下面这个class Point声明:

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classPoint...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)Point(
floatxval);
深度探索C++对象模型》读书笔记(1)
virtual~Point();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
floatx()const;
深度探索C++对象模型》读书笔记(1)
staticintPointCount();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
protected:
深度探索C++对象模型》读书笔记(1)
virtualostream&print(ostream&os)const;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
float_x;
深度探索C++对象模型》读书笔记(1)
staticint_point_count;
深度探索C++对象模型》读书笔记(1)}
;

在Stroustrup当初设计的C++对象模型中,Nonstatic data members被配置于每一个class object之内,static data members则被存放在所有的class object之外。Static和nonstatic function members也被放在所有的class object之外,Virtual functions则以两个步骤支持之:
(1)每一个class产生出一堆指向virtual functions的指针,放在表格之中,这个表格被称为virtual table(vtbl)。
(2)每一个class object被添加了一个指针,指向相关的virtual table。通常这个指针被称为vptr。vptr的设定(setting)和重置(resetting)都由每一个class的constructor、destructor和copy assignment运算符自动完成。每一个class所关联的type_info object(用以支持runtime type identification,RTTI)也经由virtual table被指出来,通常是放在表格的第一个slot处。

深度探索C++对象模型》读书笔记(1)

***虚拟继承***
继承关系也可以指定为虚拟(virtual,也就是共享的意思):

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classistream:virtualpublicios...{...};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classostream:virtualpublicios...{...};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classiostream:publicistream,publicostream...{...};

在虚拟继承的情况下,base class不管在继承串链中被派生(derived)多少次,永远只会存在一个实体(称为subobject)。例如iostream之中就只有virtual ios base class的一个实体。

***指针的类型***
下面有一个ZooAnimal声明:

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classZooAnimal...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)ZooAnimal();
深度探索C++对象模型》读书笔记(1)
virtual~ZooAnimal();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
virtualvoidrotate();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
protected:
深度探索C++对象模型》读书笔记(1)
intloc;
深度探索C++对象模型》读书笔记(1)Stringname;
深度探索C++对象模型》读书笔记(1)}
;

一个指向类的指针与一个指向整数的指针或一个指向template Array的指针之间的差异既不在其指针表示法不同,也不在于其内容(代表一个地址)不同,而是在其所寻址出来的object类型不同。也就是说,“指针类型”会教导编译器如何解释某个特定地址中的内存内容及其大小:
1.一个指向地址1000的整数指针,在32位机器上,将涵盖地址空间1000~1003。
2.如果String是传统的8-bytes(包括一个4-bytes的字符指针和一个用来表示字符串长度的整数),再加上指向vtbl的指针vptr,那么一个ZooAnimal指针将横跨地址空间1000~1015(4+8+4)。

假设Bear继承于ZooAnimal,如下所示:

深度探索C++对象模型》读书笔记(1)Bearb;
深度探索C++对象模型》读书笔记(1)ZooAnimal
*pz=&b;
深度探索C++对象模型》读书笔记(1)Bear
*pb=&b;

一个Bear指针和一个ZooAnimal指针虽然都指向Bear Object的第一个byte,但pb所涵盖的地址包含整个Bear Object,而pz所涵盖的地址只包括Bear Object中的ZooAnimal subobject。

除了ZooAnimal subobject中出现的members,你不能够使用pz来直接处理Bear的任何members。唯一例外是通过virtual机制。

深度探索C++对象模型》读书笔记(1)//不合法:cell_block不是ZooAnimal的一个member
深度探索C++对象模型》读书笔记(1)
//虽然我们知道pz当前指向一个Bearobject
深度探索C++对象模型》读书笔记(1)
pz->cell_block;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//ok:经过一个明白的downcast操作就没有问题!
深度探索C++对象模型》读书笔记(1)
((Bear*)pz)->cell_block;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//下面这样更好,但它是一个run-timeoperation(成本较高)
深度探索C++对象模型》读书笔记(1)
if(Bear*pb2=dynamic_cast<Bear*>(pz))
深度探索C++对象模型》读书笔记(1)pb2
->cell_block;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//ok:因为cell_block是Bear的一个member
深度探索C++对象模型》读书笔记(1)
pb->cell_block;

***切割引起的编译器仲裁***

深度探索C++对象模型》读书笔记(1)Bearb;
深度探索C++对象模型》读书笔记(1)ZooAnimalza
=b;//这会引起切割(sliced)
深度探索C++对象模型》读书笔记(1)
//调用ZooAnimal::rotate()
深度探索C++对象模型》读书笔记(1)
za.rotate();

为什么rotate所调用的是ZooAnimal实体而不是Bear实体?此外,如果初始化函数(应用于上述assignment操作发生时)将一个object内容完整拷贝到另一个object中去,为什么za的vptr不指向Bear的virtual table?
第二个问题的答案是,编译器在(1)初始化及(2)指定(assignment)操作之间做出了仲裁。编译器必须确保如果某个object含有一个或一个以上的vptrs,那些vptrs的内容不会被base class object初始化或改变。
至于第一个问题的答案是:za并不是(而且绝对不会是)一个Bear,它是(并且只能是)一个ZooAnimal。多态所造成的“一个以上的类型”的潜在力量,并不能够实际发挥在“直接存取objects”这件事情上。

***面对对象(OO)和基于对象(OB)***
基于对象的数据类型可以展示封装的非多态形式,但是不支持类型的扩充。一个OB设计可能比一个对等的OO设计速度更快而且空间更紧凑。速度快是因为所有的函数引发操作都在编译时期解析完成,对象建构起来时不需要设置virtual机制;空间紧凑则是因为每一个class object不需要负担传统上为了支持virtual机制而需要的额外符合。不过,OB设计比较没有弹性

***Default Constructor的建构操作***
default constructor仅在编译器需要它时,才会被合成出来。
通常来说,由编译器合成出来的default constructor是没啥用的(trivial),但有以下几种例外:
(1)带有“Default Constructor”的Member Class Object
如果一个class没有任何constructor,但它内含一个member object,而后者有default constructor,那么编译器会在constructor真正需要被调用时未此class合成一个“nontrivial”的default constructor。为了避免合成出多个default constructor,解决方法是把合成的default constructor、copy constructor、destructor、assignment copy operator都以inline方式完成。一个inline函数有静态链接(static linkage),不会被档案以外者看到。如果函数太复杂,不适合做成inline,就会合成出一个explicit non-inline static实体。
根据准则“如果class A内含一个或一个以上的member class objects,那么class A的每一个constructor必须调用每一个member classes的default constructor”,即便对于用户明确定义的default constructor,编译器也会对其进行扩张,在explicit user code之前按“member objects在class中的声明次序”安插各个member所关联的default constructor。

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classDcpey...{public:Dopey();...};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classSneezy...{public:Sneezy(int);Sneezy();...};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classBashful...{public:Bashful();...};
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classSnow_White...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)Dopeydopey;
深度探索C++对象模型》读书笔记(1)Sneezysneezy;
深度探索C++对象模型》读书笔记(1)Bashfulbashful;
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
private:
深度探索C++对象模型》读书笔记(1)
intmumble;
深度探索C++对象模型》读书笔记(1)}
;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)Snow_White::Snow_White():sneezy(
1024)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)mumble
=2048;
深度探索C++对象模型》读书笔记(1)}

深度探索C++对象模型》读书笔记(1)
//编译器扩张后的defaultconstructor
深度探索C++对象模型》读书笔记(1)
Snow_White::Snow_White():sneezy(1024)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)
//插入memberclassobject
深度探索C++对象模型》读书笔记(1)
//调用其constructor
深度探索C++对象模型》读书笔记(1)
dopey.Dopey::Dopey();
深度探索C++对象模型》读书笔记(1)sneezy.Sneezy::Sneezy();
深度探索C++对象模型》读书笔记(1)bashful.Bashful::Bashful();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//explicitusercode
深度探索C++对象模型》读书笔记(1)
mumble=2048;
深度探索C++对象模型》读书笔记(1)}

(2)“带有Default Constructor”的Base Class

如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来。
需要注意的是,编译器会将这些base class constructor安插在member object之前。

(3)“带有一个Virtual Function”的Class
另有两种情况,也需要合成出default constructor:
(a)class声明(或继承)一个virtual function
(b)class派生自一个继承串链,其中有一个或更多的virtual base classes

以下面这个程序片段为例:

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classWidge...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)
virtualvoidflip()=0;
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
}
;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
voidflip(constWidge&widge)...{widge.flip();}
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//假设Bell和Whistle都派生自Widge
深度探索C++对象模型》读书笔记(1)
voidfoo()
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)Bellb;
深度探索C++对象模型》读书笔记(1)Whistlew;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)flip(b);
深度探索C++对象模型》读书笔记(1)flip(w);
深度探索C++对象模型》读书笔记(1)}

下面两个扩张操作会在编译期间发生:
1.一个virtual function table会被编译器产生出来,内放class的virtual functions地址;
2.在每一个class object中,一个额外的pointer member(也就是vptr)会被编译器合成出来,内含相关的class vtbl的地址。
此外,widge.flip()的虚拟引发操作(virtual invocation)会被重新改写,以使用widge的vptr和vtbl中的flip()条目。

深度探索C++对象模型》读书笔记(1)//widge.flip()的虚拟引发操作的转变
深度探索C++对象模型》读书笔记(1)
(*widge.vptr[1])(&widge)

为了让这个机制发挥功效,编译器必须为每一个Widge(或其派生类)之object的vptr设置初值,放置适当的virtual table地址。

(4)“带有一个virtual Base Class”的Class

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classX...{public:inti;};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classA:publicvirtualX...{public:intj;};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classB:publicvirtualX...{public:doubled;};
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classC:publicA,publicB...{public:intk;};
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//无法在编译时期决定出pa->X::i的位置
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
voidfoo(constA*pa)...{pa->i=1024;}
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)main()
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)foo(
newA);
深度探索C++对象模型》读书笔记(1)foo(
newC);
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
}

编译器需要在derived class object中为每一个virtual base classes安插一个指针,使得所有“经由reference或pointer来存取一个virtual base class”的操作可以通过相关指针完成。

深度探索C++对象模型》读书笔记(1)//可能的编译器转变操作
深度探索C++对象模型》读书笔记(1)
//_vbcX表示编译器所产生的指针,指向virtualbaseclassX
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
voidfoo(constA*pa)...{pa->_vbcX->i=1024;}

小结:在合成的default constructor中,只有base class subobjects和member class objects会被初始化,所有其它的nonstatic data member都不会被初始化。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

***Copy Constructor的建构操作***

有三种情况会以一个object的内容作为另一个class object的初值,即object赋值、object参数传递、object作为函数返回值。
如果class没有提供一个explicit copy constructor,其内部是以所谓的default memberwise initialization手法完成的,也就是把每一个内建的或派生的data member(例如一个指针或一个数组)的值,从某个object拷贝一份到另一个object身上,不过它并不会拷贝其中的member class object,而是以递归的方式施行memberwise initialization。
copy constructor仅在必要的时候(class不展现bitwise copy semantics)才由编译器产生出来。

已知下面的class Word声明:

深度探索C++对象模型》读书笔记(1)//以下声明展现了bitwisecopysemantics
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classWord...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)Word(
constchar*);
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
~Word()...{delete[]str;}
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
private:
深度探索C++对象模型》读书笔记(1)
intcnt;
深度探索C++对象模型》读书笔记(1)
char*str;
深度探索C++对象模型》读书笔记(1)}
;

对于上述这种情况,并不需要合成出一个default copy constructor,因为上述声明展现了“default copy semantics”。
然而,如果class Word是这样声明:

深度探索C++对象模型》读书笔记(1)//以下声明未展现出bitwisecopysemantics
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
classWord...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)Word(
constString&);
深度探索C++对象模型》读书笔记(1)
~Word();
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
private:
深度探索C++对象模型》读书笔记(1)
intcnt;
深度探索C++对象模型》读书笔记(1)String
&str;
深度探索C++对象模型》读书笔记(1)}
;

在这种情况下,编译器必须合成出一个copy constructor以便调用member class String object的copy constructor:

深度探索C++对象模型》读书笔记(1)//一个被合成出来的copyconstructor
深度探索C++对象模型》读书笔记(1)
inlineWord::Word(constWord&wd)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)str.String::String(wd.str);
深度探索C++对象模型》读书笔记(1)cnt
=wd.cnt;
深度探索C++对象模型》读书笔记(1)}

一个class不展现出“bitwise copy semantics”的四种情况:
(1)当class内含一个member object而后者的class声明有一个copy constructor时(无论是被明确声明或被合成而得)
(2)当class继承自一个base class而后者存在有一个copy constructor时

对于前两种情况,编译器必须将member或base class的“copy constructor调用操作”安插到被合成的copy constructor中。

(3)当class声明了一个或多个virtual functions时
由于编译器要对每个新产生的class object的vptr设置初值,因此,当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了。
当一个base class object以其derived class的object内容做初始化操作时,其vptr复制操作必须保证安全,而如果依旧采用bitwise copy的话,base class object的vptr会被设定指向derived class的virtual table,而这将导致灾难。

(4)当class派生自一个继承串链,其中有一个或多个virtual base classes时
当一个class object以其derived classes的某个object作为初值时,为了完成正确的virtual base class pointer/offset的初值设定,编译器必须合成一个copy constructor,安插一些码以设定virtual base class pointer/offset的初值,对每一个member执行必要的memberwise初始化操作,以及执行其他的内存相关操作。在这种情况下,简单的bitwise copy所做的就远远不够了。

***优化***

(1)在使用者层面做优化
定义一个“计算用”的constructor:

深度探索C++对象模型》读书笔记(1)Xbar(constT&y,constT&z)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)Xxx;
深度探索C++对象模型》读书笔记(1)
//...以y和z来处理xx
深度探索C++对象模型》读书笔记(1)
returnxx;
深度探索C++对象模型》读书笔记(1)}

上述constructor要求xx被“memberwise”地拷贝到编译器所产生地_result之中。故可定义如下的constructor,可以直接计算xx的值:

深度探索C++对象模型》读书笔记(1)Xbar(constT&y,constT&z)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)
returnX(y,z);
深度探索C++对象模型》读书笔记(1)}

(2)在编译器层面做优化
比较以下三处初始化操作:

深度探索C++对象模型》读书笔记(1)Xxx0(1024);
深度探索C++对象模型》读书笔记(1)Xxx1
=X(1024);
深度探索C++对象模型》读书笔记(1)Xxx2
=(X)1024;

其中,xx0是被单一的constructor操作设定初值:

xx0.X::X(1024);

而xx1和xx2则是调用两个constructor,产生一个暂时性的object并设以初值1024,接着将暂时性的object以拷贝建构的方式作为explicit object的初值,最后还针对该暂时性object调用class X的destructor:

深度探索C++对象模型》读书笔记(1)X_temp0;
深度探索C++对象模型》读书笔记(1)_temp0.X::X(
1024);
深度探索C++对象模型》读书笔记(1)xx1.X::X(_temp0);
深度探索C++对象模型》读书笔记(1)_temp0.X::
~X();

由此可以看出,编译器所做的优化可导致机器码产生时有明显的效率提升,缺点则是你不能安全地规划你的copy constructor的副作用,必须视其执行而定。
那么,究竟要不要copy constructor?copy constructor的应用,迫使编译器多多少少对你的程序代码做部分优化。尤其是当一个函数以传值(by value)的方式传回一个class object,而该class有一个copy constructor时,这将导致深奥的程序转化。

举个简单的例子,不管使用memcpy()或memset(),都只有在“classes不含任何由编译器产生的内部members”时才能有效运行。而如下的class由于声明了一个virtual function,编译器为其产生了vptr,此时若使用上述函数将导致vptr的初值被改写。

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classShape...{
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)
//这会改变内部的vptr
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
Shape()...{memset(this,0,sizeof(Shape));};
深度探索C++对象模型》读书笔记(1)
virtual~Shape();
深度探索C++对象模型》读书笔记(1)
//...
深度探索C++对象模型》读书笔记(1)
}
;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//扩张后的constructor
深度探索C++对象模型》读书笔记(1)
Shape::Shape()
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)
//vptr必须在使用者的代码执行之前先设定妥当
深度探索C++对象模型》读书笔记(1)
_vptr_Shape=_vtbl_Shape;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//memset会将vptr清为0
深度探索C++对象模型》读书笔记(1)
memset(this,0,sizeof(Shape));
深度探索C++对象模型》读书笔记(1)}

***成员的初始化队伍***

下列情况中,为了让你的程序能够被顺利编译,你必须使用member initialization list:
(1)当初始化一个reference member时;
(2)当初始化一个const member时;
(3)当调用一个base class的constructor,而它拥有一组参数时;
(4)当调用一个member class的constructor,而它拥有一组参数时。

成员初始化列表有时可带来巨大的性能提升,不妨看看下面一组对比:

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classWord...{
深度探索C++对象模型》读书笔记(1)String_name;
深度探索C++对象模型》读书笔记(1)
int_cnt;
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)Word()
...{
深度探索C++对象模型》读书笔记(1)_name
=0;
深度探索C++对象模型》读书笔记(1)_cnt
=0;
深度探索C++对象模型》读书笔记(1)}

深度探索C++对象模型》读书笔记(1)}
;
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//其constructor可能的内部扩张结果
深度探索C++对象模型》读书笔记(1)
Word::Word()
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)
//调用String的defaultconstructor
深度探索C++对象模型》读书笔记(1)
_name.String::String();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//产生暂时性对象
深度探索C++对象模型》读书笔记(1)
Stringtemp=String(0);
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//“memberwise”地拷贝_name
深度探索C++对象模型》读书笔记(1)
_name.String::operator=(temp);
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)
//摧毁暂时性对象
深度探索C++对象模型》读书笔记(1)
temp.String::~String();
深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)_cnt
=0;
深度探索C++对象模型》读书笔记(1)}

若使用成员初始化列表:

深度探索C++对象模型》读书笔记(1)//较好的方式
深度探索C++对象模型》读书笔记(1)
Word::Word:_name(0)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)_cnt
=0;
深度探索C++对象模型》读书笔记(1)}

深度探索C++对象模型》读书笔记(1)
深度探索C++对象模型》读书笔记(1)Word::Word()
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{
深度探索C++对象模型》读书笔记(1)
//调用String(int)constructor
深度探索C++对象模型》读书笔记(1)
_name.String::String(0);
深度探索C++对象模型》读书笔记(1)_cnt
=0;
深度探索C++对象模型》读书笔记(1)}

需要注意的是,成员初始化列表中的项目是依据class中的member声明次序,一一安插到explicit user code之前的。(因此,对于表达式两边均出现member的情形要特别小心)
下面就给出错误实例:

深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)classX...{
深度探索C++对象模型》读书笔记(1)
inti;
深度探索C++对象模型》读书笔记(1)
intj;
深度探索C++对象模型》读书笔记(1)
public:
深度探索C++对象模型》读书笔记(1)
//i比j先被赋值,而此时j尚未有初值
深度探索C++对象模型》读书笔记(1)
X(intval):j(val),i(j)
深度探索C++对象模型》读书笔记(1)深度探索C++对象模型》读书笔记(1)
...{}
深度探索C++对象模型》读书笔记(1)...
深度探索C++对象模型》读书笔记(1)}
;