??xml version="1.0" encoding="utf-8" standalone="yes"?>
5.1 “无l承?情况下的对象构?br />1. 普通类?和C相同)
2. 抽相数据cd
3. 为承作准备
5.2 l承体系下对象的构?br />1. 通用l承构造规?br /> (1) 在成员初始化列表中的data members初始化操作会被放qconstructor的函数本w,q以members的声明顺序ؓ序?br /> (2) 如果有一个member没有出现在初始化列表中,但它有一个default constructorQ那么default constructor会被调用?br /> (3) 如果class object 有virtual table pointer(s)Q???必须被设定初始|指向适当的virtual table(s)?br /> (4) 在那之前Q所有的上一层的base class constructors必须被调? 以base class的声明顺序ؓ序
(5) 在那之前Q所有的virtual base class constructors必须被调用,从左到右Q从最深到最?
2. 虚拟l承(virtual Inheritance)
如对于下面的c?
可能的{换是q样?
//
对于如下的承层?
class Vertex : virtual public Point { ... };
class Vertex3d : public Point3d, public Vertex { ... };
class PVertex : public Vertex3d { ... };
cPoint3d的构造可能是:
//
对于Vertex3d的构造可能是如下:
3. vptr初始化语意学(The Semantics of the vptr Initialization)
(1) 构造函数执行算?br /> I. 在derived class constructor ? 所有的"virtual base classes" ?"上一层base class"的constructors会被调用.
II. 上述完成之后, 对象的vptr(s)会被初始? 指向相关的virtual talbe(s).
III. 如果有成员初始化列表的话, 在constructor体内扩展开? q必dvptr被设定之后才能进?以免有一个virtual member function被调?br /> IV. 最? 执行E序所提供的代?
(2) CZ:
For example, given the following user-defined PVertex constructor:
可能一个扩展如?
5.3 对象的复?Object Copy Sematics)
1. Copy constructor operator 在以下几U情况下不会表现? bitwisecopy
(1) class 内带有一个member object, 而其cL一个copy constructor operator时?br /> (2) 当一?class 的base 有一个copy assignment operator时?br /> (3) 当类声明了Q何一个virtual functions时?br /> (4) 当classl承自一个virtual base class?br /> 2. 合成CZ
5.4 对象的功?/p>
5.5 析构语意?Semantics of Destruction)
1. 析构函数生成原则Q?br /> 如果cL有定义destructor, 那么只有在class内带的member object(或是class自己的base class)拥有destructor的情况下Q编译器才会自动的合成一个来。否则,destructor会被视ؓ不需要,也就不需要被合成(当然更不需要被调用)
2. 析构调用q程
(1) destructor的函数本w首先被执行?br /> (2) 如果class拥有member class objects, 而后者拥有destructors, 那么它会以其声明序的相反顺序被调用.
(3) 如果object内带一个vptrQ?则现在被重新讑֮Q指向适当的base class的virtual table.
(4) 如果M直接?上一?novirtual base classes 拥有destructorQ那么它会以其声明顺序相反的序调用
(5) 如果有Q何的virtual base classes 拥有destructorQ?而当前讨论的q个class是最末端的classQ?那么它们会以其原来的构造顺序相反的序被调?
4.1 Member 函数的各U调用方?br />1. Nonstatic Member Function(非静态成员函?
要做如下的{?
(1) 改写函数的原? 安插一个参数到member function? 用于提供一个存取管? 使class object 得以调用该函? q个额外的指针称之ؓ: this指针.
?
Point3d::magnitude() 会{换ؓ: Point3d::magnitude(Point3d *const this)
(2) 对函数内每一个针对nostatic data member的存取操作改l由this指针来存?
?
return sqrt(
this->_x * this->_x + this->y * this->_y + this->_z * this->_z;
(3) 对member function 重写一个外部函? 对函数名U进?mangling"处理, 使它生成一个独一无二的名U?br />
2. Virtual Member Function(虚拟成员函数)
例如:
ptr->normalize();
它将被{化ؓ如下的调?
( * ptr->vptr[ 1 ])( ptr );
q里有几点说明:
i. vptr是由~译器生成的指针Q指向virtual table
ii. 1 q里是virtual table slot 的烦引|它关联到nomalizeq个函数
iii. W二个ptr表示this指针
3. Static Member Function(静态成员函?
如果Point3d::normalize()是一个static member function的话Q这两个调用会{化ؓ一般的操作Q?br /> obj.normalize();
ptr->normalize();
转化为:
// obj.normalize();
normalize__7Point3dSFv();
// ptr->normalize();
normalize__7Point3dSfv();
4.2 Virtual Member Functions(虚拟成员函数)
1. 单一l承下的Virtual Functions
一个多态的class object w上增加两个members:
I. 一个字W串或数? 表示class的类?br /> II. 一个指针,指向某个表格Q表g带有E序的virtual function的执行时期地址
对于一个active virtual function包括下面三个内容Q
I. q个class 所定义的函数实? 它会改写一个可能存在的base class virtual function 函数实体.
II. l承自基cȝ实体Q q是在derived class 军_不改写virtual function 时才会出现的情况
III. 一个pure_virtual_called()函数实体Q它既可以扮演pur virtual function的空间保卫者角? 也可以当做执行期异常函数.
2. 多重l承下的Virtual Functions
q种l承涉及到要调整this指针Qƈ且要求不止一个vtbl和vptrQ同时要好几个这U虚表和指针
3. 虚拟l承下的Virtual Functions
4.3 函数的效?/p>
4.4 指向Member Functions的指?Pointer-to-Member Functions)
1. 指向一般成员函数的指针(Nostatic member and novirtual member function)
取一个nostatic member function的地址, 如果该函数是novirtual, 则得到的l果是它在内存中真正的地址, 然而这个地址也不是完全的, 它也需要绑定到某个class object的地址? 才能够调用该函数. 所有的nostatic member functions都要对象的地址(用this指出).
例如:
double (Point::*pmf)(); // 定义一个成员函数指?br /> pfm = &Point::y; // 初始化这个指针ؓ
(ptr->*pfm)() ; // 调用? ~译器{化ؓ: (pfm)(ptr)
2. 支持"指向Virtual Member Functions"的指?br /> 对于virtual function, 其地址在编译时期是未知? 所能知道的仅是virtual function在相关的vitual table 中的索引? 也就是说对于一个virtual member function取其地址, 所能获得的只是一个烦引?
所以如?
pmf = &Point::z(); // 获得的是索引? 调用?
(ptr.->pmf)() // 会{化ؓ: (* ptr->vptr[(int)pfm] (ptr)
3. 在多重承下,指向Member Functions的指?br /> 比较复杂, 定义了一个结构支持这们的操作
4. 指向 Member Functions 指针的效?/p>
4.4 Inline Functions
1. inline functions的生成条?/p>
2. 对Ş式参数的处理(Formal Arguments)
例如:
用下面的方式q行处理:
(1) 直接的参数替?br /> //(1) simple argument substitution
minval = val1 < val2 ? val1 : val2;
(2) 如果实际参数是一个常量表辑ּ(const expression), 我们可以在替换前完成对它的求值操?
//(2) constant folding following substitution
minval = 1024;
(3) 带有副作用的实际参数, 引入临时性的对象
//(3) side-effects and introduction of temporary
int t1;
int t2;
minval = ( t1 = foo() ), ( t2 = bar() + 1 ), t1 < t2 ? t1 : t2;
3. 对inline函数带有局部变量的处理(Local Variables)
?
inline int min( int i, int j )
{
int minval = i < j ? i : j;
return minval;
}
对于如下的调?
{
int local_var;
int minval;
// ...
minval = min( val1, val2 );
}
转换可能的结果是:
CZ代码Q?br />class X {};
class Y : public virtual X {};
class Z : public virtual X {};
class A : public Y, public Z {};
一个类对象的大受三个因素的媄?br />i. 语言本n所造成的额外负?overhead), 当语a要支持virtual base class Ӟ׃D一些额外的负担.
ii. ~译器对Ҏ情况所提供的优化处? 如virtual base class class X subobject ?bytes大小会出现在子类Y, Z的n?
? sizeof(Y) = sizeof(Z) = 4(8) // q里?(8)和编译器相关
有时候有的编译器会用empty virtual base class 技术来优化, VC是采用q一技术的, q样virtual base class ׃用占用大了.
iii. Alignment 的限? Y和Z的大本来大都?, 加上virtual base subobject?bytes的大共5个字? 但实际上L8bytesQ这里就是受到字节对齐的影响.
C++对象模型Ҏ据的存放l构?
i. 把nonstatic data members直接的放在class object之中, 对?不管是virtual ?nonvirtual base class )而来的nonstatic data members也是q一L.
ii. 没有强制定义光的排列顺?br />iii. 对static data members, 则被攄在一个global data segment? 不会影响单个cȝ大小, q且只保存一份实? (template有所不同)
3.1 Data Member的绑?The Binding of Data Member)
CZ代码:
在早期的~译器中会出? 不过?C++2.0后就不会? 在C++2.0? 采用的是"rewriting rule" == "member scope rsolution rule" 规则来处理它.
以前的编译器? float X() const { return x; }, 它不知道要返回哪一个x, q里它会q回全局?extern float x, 所以是不正的. 后来的编译器是会在整个class的声明都出现了后才会分析member functions, 所以它不会现错.
对于下面的例子还是会出错, 因ؓ对于member functions signatures的分析不会到cd成以? 而是W一ơ出现的时候就会分析的. 如下面的:
所以最好始l的?nested type declare" 攑֜cȝ起始? (q在STL中好像最明显, 都是先声明的)
3.2 Data Member的布局 (Data Member Layout)
CZ代码:
Data Member的布局按如下的规则:
i. Nonstatic data member 在class object中的排列序和被声明的顺序是一L, M中间介入的static data member都不会被放进对象的布局?
ii. 要求在同一access section?较晚出现的members在class object中有较高的地址"q一条g可?
iii. ~译器可能会合成一些内部用的data members, 以支持整个对象模? 如vptr指针. 对于它的具体位置, C++ Standard 没有规定, q译器产商自己军_. 不过传统上一般是攑֜所有声明的members的最? 也有把vptr攑֜所有class object的最前端?
3.3 Data Member的存?br />CZ:
Point3d origin, *pt = &origin;
origin.x = 0.0;
pt->x = 0.0
1. Static Data Members的存?br /> 每一个static data member只有一个实体,存在于程序的data segment中。每ơ程序取用这个static data member的时候,׃被{化ؓ对该实体的唯一的extern实体的直接参考操? 用指针存取一个数和用对象d取一个数是一L?br />
2. Nostatic Data Members的存?br /> Nostatic data member 直接存放在每一个class object之中Q除非经由明的或暗ȝclass objectQ否则没有办法直接的存取它们?br /> 例如:
Point3d Point3d::translate( const Point3d &pt )
{
x += pt.x;
y += pt.y;
z += pt.z;
}
实际l过转换后ؓQ?br /> Point3d Point3d::translate( Point3d *const this, const Point3d &pt )
{
this->x += pt.x;
this->y += pt.y;
this->z += pt.z;
}
对nostatic data member的访问是q样?
origin._y = 0.0;
实际转换操作?
&origin + (&Point3d::_y - 1 );
注意: q里?1操作。指向data member的指针,其offset值L被加?Q?q样可以使编译系l区分出:
i. 一个用以指出class的第一个member的data member的指?
ii. 一个没有指ZQ何member的data member的指?
如果是virtual l承的话Q就可以不一样了Q可能要多加层的讉K? 也可能要到运行时才能军_Q由~译器所军_.
3.4 “扎쀝与Data Member
CZ数据:
C++的承模?
在C++的承模型中Q?一个derived class object 所表现出来的东西,是其自己的member加上其base class(es) member的d。对于数据成员出现的序在C++ Standard 中没有规定。从下面几个斚w来讨论数据?
i. 单一l承且不含有virtual functions
ii. 单一l承q含有virtual functions
iii. 多重l承
iV. 虚拟l承
1. 只要l承不要多?Inheritance Without Polymoophism)
l承一般不会增加空间或存取旉。但l承有时会有q样两种情况出现:
i. l验不的h有时可能会重复的设计一些相同的函数.
ii. 把一个类分解为多层,有可能会Z表现class的体pL象化Q所需要的I间膨胀?br /> 因ؓC++语言要保? 出现在derived class 中的base class subobject 有其完整原样性?/p>
2. 加上多?Adding Polymorphism)
如:
//
要支持多态,Point2d数据成员要做如下的工?
i. 导入一个和Point2d有关的virtual table(vtbl), 存放它声明的每一个virtual function的地址
ii. 在每个class object中导入一个vptr, 提供执行期的链接Q每个object都能扑ֈ相应的virtual table.
iii. 加强construtor, 使它能够为vptr讑֮初|让它指向class所对应的virtual table.
iV. 加强destructor, 使它能够Ҏ"指向class的相?virtual table" 的vptr.
Figure 3.3. Data Layout: Single Inheritance with Virtual Inheritance
3. 多重l承(Multiple Inheritance)
4. 虚拟l承(Virtual Inheritance)
Class 中如果含一个或多个virtual base class subobjects, 它将被分Z个部? 一个不变的局部和一个共享的局?
i. 不变的局部中的数据,不管后如何衍化QL固定的offsetQ?所q一部分的数据可以直接的被存取?
ii. ׃n的局部,所表现的就是virtual base class subobject, q一部分的数据会因ؓ每次z的操作而有变化, 所以它们只能间接的存取?/p>
3.5 对象成员的效?Object Member Efficiency)
3.6 指向数据成员的指?Point to Data Members)
对于有多个成员对象的c,则按成员对象在类的声明中的顺序调用它们的构造函数?br />
2. 带有 Default Constructor ?Base Class(带有~省构造函数的基类对象) 指的是一个没有Q何constructorsrs的类z自一个带有“default constructor”的基类?如果cd在多个构造函数的话,那么~译器也会扩充现有的每一个构造函敎ͼ调用基cȝdefault constructor的代码加入到其中?
3. 带有一?Virtual Function ?Class
q样的virtual Function可以使承来的或直接定义的?~译器在~译期间会做如下工作
a. 生成一个virtual Function talbe(vtbl), 用来存放class 的virtual function的地址
b. 在每一个class object中,d一个额外的pointer member(vptr)Q它指向class vtbl的地址
4. 带有一?Virtual Base Class ?Class
q种情况是在导出cȝ安插一个virtual base classes的指针,用来在运行时存取virtual base class 的成员。例如:
注意: 在合成的default constructor中,只有base class subobjects和member class objects会被初始化,其它所有的nostatic data member都不会执行初始化的操作.要我们自己手动的L行初始化的操作.
其实把握一个原则:要不要合成一个构造函敎ͼ依据~译器的需要与否,合成出来的constructor也只是完成那些编译器所需求?br />
两个错误的观点:
i. M时候,如果没有定义default constructorQ就会被合成一个出来?而是Ҏ~译器的需要决定是否生成一个deault constructor)
ii. ~译器生成出来的default constructor会明的初始化每cM的每一个data member.
2.2 Copy Constructor的构建操?br /> 有三U情况下会有一个Copy Constructor的操作:
i. 对一个object做明的初始化操作 ?/ class X{ ....}; X x; X xx = x;
ii. 当某个object作ؓ参数传递给某个函数 // void foo(X x) { .... }
iii. 函数传回一个class object对象 // void foo(){ X xx; return xx;}
1. Default Memberwise Initialization(~省的按成员初始?
如果一个类没有explicit copy constructor? class object在执行copy constructorӞ内部以default member initialization的手法来完成Q就是把每一个内建或者派生而来的data member的g某个object 拯一份到另一个objectw上Q对member class object是以递归的方式调用memberwise initialization的?br />
q种初始化是通过在必要时生成一个copy constructor或通过 bitwise copy 来完成的?如果要通过bitwise来完成这q样的操作,那么必须表现出bitwise copy semantise.
C++ Standard中也把copy constructor分成trial 和notrivial两种cdQ只有notrivial的实体才会被合成于程序中。决定一个copy constructor是否为trivial在于cL否表现出所谓的"Bitwise copy sematics".
2. Bitwise Copy Semantics
?bitwise copy sematics 下,不会构造出一个default copy constructor. 例如Q?br />
3. 不表现出Bitwise Copy Sematics的特性的情况
以下的几U情况class不表现出 Bitwise Sematics:
a. 当class 内含有一个member object, 而后者的class 声明有一个copy constructor?合成或声明的都可?Q就不表现出Bitwise Sematics
b. 当class l承自一个base class, 而后者存在有一个copy constructor(合成或声明的都可?, ׃表现出Bitwise Sematics.
c. 当类声明了一个或多个virtual function?br /> d. 当classz自一个承创串链Q其中有一个或多个virtual base classes? cd不表现出Bitwise Sematics
q四U情况和default constructor生成的是否一个trivial是一L, 也是满~译器的需求?br />
4. 重新讑֮Virtual Table 的指?br /> 对于以上Q当cd明了一个或多个virtual function的情况,E序会ؓcd象做如下两个扩张操作Q?br /> (1) 增加一个vtbl的指针,内含virtual function的指针地址?br /> (2) 在类对象内安插一个vptr指针Q指向virtual function table.
对于把一个base class object 以其derived class object的值初始化操作的时候,会发生切?sliced)操作。这时也要保证vtbl的正和vtpr的正性?br />
5. 处理Virtual Base Class Subobject
如果一个class object是以另外一个object作ؓ初始值的话,而后者又有一个virtual base class suboject的话Q那么也会bitwise copy sematics失效。例如:
2.3 E序转化语义?Program Transformation Semantics)
指的是程序在执行的是候要做的cd和操作的转换?下面例子反映了三U{换操作:
1. 明确的初始化操作(Explicit Initialization)
例如Q?br />
对程序做两个阶段的{化:
i. 重写每一个定义,其中的初始化操作都会被剥?br /> ii. cȝCopy constructor操作会被安插q去
转化后可能成L(伪码)Q?br /> q也是说前面三U的初始化操作都会{换成Explicit Initialization.
2. 参数的初始化(Argument Initialization)
例如下面Q?br />
q就是ؓ什么当传值到孙数的时候,不会把函数内对传入值的操作l果不会被修改,因ؓ它在函数内本来修改的׃是外部传人的那个变量?/p>
3. q回值的初始?Return Value Initialization)
例如Q?
X bar()
{
X xx;
// ....
return xx;
}
转化为如下伪?
void bar(X& __result)
{
X xx;
xx.X::X(); // ~译器生成的一个缺省的构造函数调?/p>
// 处理 ....
__result.X::X(xx); // ~译器生成的copy constructor调用操作
return;
}
如下的操作{化ؓQ?br /> void foo()
{
X xx = bar();
}
被{化ؓQ?br /> void foo()
{
X xx;
bar(xx); // 注意q里不用执行 default constructor
}
针对q种转换Q可以从使用层面上进行优化和在编译器层面上做优化操作。在~译器层面上的操作就是采用NRV(Named Return Value)技术?br /> 如:
x bar()
{
X xx;
// ....
return xx;
}
优化为:
void bar(X& __result)
{
__result.X::X();
//.... 直接的处?__result
//
return ;
}
2.4 成员初始化队?Member Initialization List)
q里有两点要注意Q?br /> 1. 以下的四U情况下必须使用 member intialization list
I. 当初始化一个reference member?br /> II. 当初始化一个const member?br /> III. 当初始化一个base class 的constructorQ而它拥有一l参数时
IV. 当调用一个member class的constructor, 而它拥有一l参数时
其它情况下可以用在构造函数内初始化也可以
2. 关于初始化的ơ序问题Q?br /> ~译器的初始化的序是按member声明ơ序来依ơ的初始化的。它会安插一些代码到构造函数内Qƈ攑֜M其它用户初始化的代码之前.
2. C++语言数据和方?/strong>
C++中是通过ADT(Abstract Data Type, ADT)来实现的?C++可以在不同层ơ上q行抽象Q造成的复杂度可能也不一栗?br /> 书中从简单到复杂四个层次的抽? 单类、ѝ一个参数的Template、两个参数的模板?br />
?C++加上装后的布局成本(Layout Costs for Adding Encapsulation)
1. C++中的对象的布局
a. data member: 直接的包涵在每一个class object(注意: cd象,不是c?之中Q这和C struct的情冉|一L
b. member function: 它不出现在class object 之中.
non-inline member: 它会产生一个行数的实体. 如果是非static的funciton, 每个function会加上一个this指针作ؓfunction的第一个参?
inline member: 会在每一个用者n上生一个函数的实体。这一般是Z提高效率?br />
2. C++布局和存取上的额外开销
a. virtual function 机制: 用以支持一个有效的"执行期绑?runtime binding)"
b. virtual base class
? C++对象模型(The C++ Object Model)
1. 单对象模?A Simple Object Model)
q种模型中,每个object是一pd的slots, 每个slot指向一个member. 每个member按其x的次序各占用一个slot. q里的member包括data member ?function member. 每个member是通过slot的烦引来讉K的?br /> 具体的模型参?
2. 表格驱动模型(A Table-driven Object Model)
q种模型中把class object的members分组攑֜一个data member table 和一个function member table中,class object内含两个指向table的指? member function table 是一pd的slots, 每个slot指向一个function member. data member table 则是直接的包涉|data本n?br /> 具体的模型参?
3. C++对象模型(The C++ Object Model)
C++的对象模型如?
a. nostatic data members 被直接的配置在每一个class object之内?br /> b. static data member 、static ?nonstatic function members全部被放在所有的class object 之外?br /> c. virtual functions 则是以下列步骤支持的Q?
i. 每一个class 产生一堆指向virtual functions的指针,攑֜表格之中Q我们称q个表格为:virtual table(vtbl).
ii. 每个得class object 被添加了一个指针,指向相关的virtual tableQ我们把class object的这个指针称之ؓvptr(virtual pointer)Q这个vptr的设定和重置是由cȝconstructor、destructor ?copy assignment q算W自动完成的Q每个类的type_info object也是l由virtual table指出的,通常是放在表格的W一个slot处?br /> 具体的模型参?
d. 加上l承(Adding Inheritance)
?A Simple Object Model 中,每一个基cd以被derived class object的一个slot指出Q该slot内含base class subobject的地址?br /> 在虚拟承的情况下,base class 不管在扉K中被z多少ơ,永远只有一个实?subobject). 书中以iostreaml承体系说明?/p>
C++中的base class subobject的data members直接攄于derived class object中。那么它的function members是怎么处理的呢Q?我没有理解这?
对于virtual base class, C++ 2.0 是在class object中添加一个关?virtual base class object的指针?/p>
e. 对象模型对程序的影响
我觉得书上的q段代码非常好的体现了不同模型对E序的媄?br /> 预定?class X 如下Q?br />
? 关键词所带来的差?A Keyword Distinction)
讨论了class ?struct 的差异和选择
? 对象的差? A Object Distinction)
1. C++E序设计模型支持三种programming paradigms.
a. E序模型(procedural model) 是?C 一栯行编E?br /> b. 抽象数据cd模型(abstract data type model, ADT) 用对象进行编E?br /> c. 面向对象模型(object-oriented model)
模型中有一些彼此相关的cdQ通过一个抽象的base class被封装v?也就是:接口)。类型之间的操作是通过接口q行的?/p>
Ua的以一Uparadigm写程序是好的.(哈哈Q好像这不太可能Q我q做不到)
? 面向对象模型(object-oriented model)
a . C++中多态支持性的支持是通过: pointer ?reference来实现的.
多态通过下面三种Ҏ来支?
i. l由一l隐含的转化操作: shape *ps = new circle();
ii. l由virtual function 机制 ps->rotate();
iii. l由 dynamic_cast和typeid来支?
if(circle *pc = dynamic_cast<circle*>(ps)) ...
多态内存需?br /> i. ?nonstatic data members 的d大小
ii. M字节寚w的额外填?padding)
iii. 支持virtual 而生的额外负担
b. 指针的类?br /> "指向不同cd的各指针"的差异,不在于其指针的表C法不同Q也不在于其内容的不同, 而是其寻址出来的object的类型不同。也是?指针cd"会教导编译器如何解释某个特定地址中的内存内容及其大小.
c. 加上多态之?Adding Polymorphism)
以如下ؓ?
具体的内存布局如?br />
//
现有
以上每个都指向Bear object的第一个byteQ?font color="#ff0000" size="2">光的差别是Qpb所늛的地址包含整个的Bear object, 而pz所늛的地址只包含Bear object中的 ZooAnimal subobject部分。你只能用pz来处理Bear中的virtual functions, 而不能直接的处理Bear中的其他Mmembers.
注意pz的类型将在编译时定以下两点:
i. pz固定的可用接?br /> ii. pz的接口的access levelQ因为子cȝaccess level可能是不同于基类的,~译时会是否可以{换?/p>
e. 对象赋值问?br />
q里有两个问?br /> i. zaZ么调用的是ZoomAnimal::rotate的实体而不?Bear的实?
{:zaq不是一个Bear, 它只是一个ZoomAnimal, 多态的q种Ҏ不能用在直接存取的objects上。所以?za.rotate()调用只能?ZooAnimal::rotate()
ii. 如果初始化函数将一个object的内容完全拷贝到另一个object中去Qؓ什么za的vpt不是指向Bear的virtual table呢?
{:~译器在初始化或赋值操作时Q如果某个object含有一个或多个vptrs, 那么q些vptrs的内容不会被原对象初始化或改?
例如上例?ZooAnimal za = b, q里的vptrq不会被 b 的vptr所替代.