【深度探索C++对象模型】第二章 构造函数语意学(中)
2014-08-30 12:08
253 查看
第二章 构造函数语意学(The Semantics of Constructors)(中)
—— 本书作者:Stanley B.Lippman
(接上篇)
三、Copy Constructor 的建构操作
学习目标:
什么是 拷贝构造函数(Copy Constructor) ?
何时会调用 拷贝构造函数(Copy Constructor)?
编译器什么时候会为我们合成一个“有用”的拷贝构造函数?
何时会调用拷贝构造函数(Copy Constructor)?
当一个 对象 通过另一个对象来初始化时,前者的 拷贝构造函数 会被调用。
1. 第一种情况: 初始化。
2. 第二种情况:对象 作为函数参数。
3. 第三种情况:对象 作为函数返回值。
拷贝构造函数的表现形式:
Default Memberwise Initialization
如果你没有给你的 class 显示的定义 拷贝构造函数,那么在发生需要调用拷贝构造函数的情形时,内部是通过 default Memberwise Initialization 手法完成的。
Default Memberwise Initialization 看起来像什么样子呢?
【注】当你的类里有指针成员的时候,要格外小心。具体什么问题,可参考 Effective C++ 里的条例。
当我们没有显示定义 拷贝构造函数 时,编译器什么时候才会为我们合成一个?
什么时候一个 class 不展现出 Bitwise Copy Semantics 呢?有四种情况:
1. 当 class 内含一个 成员对象,且该成员对象的类声明有一个 copy constructor 时(不一定是明确声明的,也可能是由编译器合成的 copy constructor)。如,你的 class 里有一个 std::string 成员时(std::string 的copy
constructor 不是合成的,是明确声明的),这时如果你没有明确声明一个 copy constructor 编译器会为你合成一个。
2. 当 class 继承自一个 base class ,而后者存在一个 copy constructor 时(合成的或者声明的都算)。
3. 当 class 声明了一个或多个 virtual functions 时。
4. 当 class 的继承串链中,其中有一个或者多个 virtual base classes 时。
重新设定 Virtual Table 的指针__vptr
前面曾学到过,C++对象模型是如何支持 virtual 机制的。是通过在 class 内安插了一个 vptr 指向其相应的 vtbl。因此,如果编译器不能正确的初始化 vptr,将导致严重的后果。所以,当一个 class 有 virtual functions 时,就不在展现出 Bitwise semantics。于是编译器将会合成一个 copy constructor
来初始化 vptr。下面是列子。
【注】一个 class 对应一个 vtbl。相同 class 的 objects 的 vtbl 相同,也即 vptr 指向的地址相同。
也就是说:合成出来的 Base class 的拷贝构造函数会明确设定其对象的 vptr 指向 Base class 的 vtbl,而不是直接从右手边的 class object 中的 vptr 地址拷贝过来。
处理 Virtual Base Class Subobject
对于虚拟继承(即:继承自一个虚基类),编译器必须维护 派生类中虚基类的位置(要理解这个“位置”的含义,首先你的了解继承后的对象模型是怎样的)。
直接按位拷贝可能会破坏这个"位置"。
当我们以一个Raccoon对象作为另一个Raccoon对象的初值时,不会有任何问题,默认的Bitwise semantics就足以搞定。但当我们以Derived class:RedPanda作为Raccoon的初值时,就需要编译器合成出一个 拷贝构造函数,以正确的通过 RedPanda(子类) 初始化 Raccoon(父类) 。
【说明】近期项目上线,工作较忙,更新像蜗牛!后续加快节奏!
—— 本书作者:Stanley B.Lippman
(接上篇)
三、Copy Constructor 的建构操作
学习目标:
什么是 拷贝构造函数(Copy Constructor) ?
何时会调用 拷贝构造函数(Copy Constructor)?
编译器什么时候会为我们合成一个“有用”的拷贝构造函数?
何时会调用拷贝构造函数(Copy Constructor)?
当一个 对象 通过另一个对象来初始化时,前者的 拷贝构造函数 会被调用。
1. 第一种情况: 初始化。
class X { // ... }; X x; // 定义一个 X 对象 X xx = x; // xx 用 x 来初始化,但如果是: X xx; xx = x; 这里调用的就是 operater = 了,原因很明显,前面才是初始化,这里是赋值。 |
extern void foo(X x); void bar() { X xx; // 以 对象 xx 作为 foo(X x) 的参数x的初值。会调用 x 的Copy Constructor,这里的 x 是一个临时对象。 foo(xx); } |
X foo_bar() { X xx; // ... return xx; } |
class X { X( const X& x); // 也可能是多参形式,第二个参数及其后的参数有默认值 // X( const X& x, int other = 0); } |
如果你没有给你的 class 显示的定义 拷贝构造函数,那么在发生需要调用拷贝构造函数的情形时,内部是通过 default Memberwise Initialization 手法完成的。
Default Memberwise Initialization 看起来像什么样子呢?
class A { public: A(const A& a); private: int a; int b; } // 编译器合成的 Copy Constructor 可能像这样: A::A(const A& a) { this->a = a.a; this->b = a.b; } |
当我们没有显示定义 拷贝构造函数 时,编译器什么时候才会为我们合成一个?
当一个 class 没有显示定义 copy constructor 的时候,编译器会在必要的时候为这个class合成出来。所谓的必要时,是指 class 没有展现出 Bitwise Copy Semantics(位逐次拷贝) 时。而当 class 展现出 位逐次拷贝 时,并不需要合成一个 Copy Constructor,默认的 Memberwise Initialization 就已足够(但有风险:指针问题)。 |
1. 当 class 内含一个 成员对象,且该成员对象的类声明有一个 copy constructor 时(不一定是明确声明的,也可能是由编译器合成的 copy constructor)。如,你的 class 里有一个 std::string 成员时(std::string 的copy
constructor 不是合成的,是明确声明的),这时如果你没有明确声明一个 copy constructor 编译器会为你合成一个。
2. 当 class 继承自一个 base class ,而后者存在一个 copy constructor 时(合成的或者声明的都算)。
3. 当 class 声明了一个或多个 virtual functions 时。
4. 当 class 的继承串链中,其中有一个或者多个 virtual base classes 时。
重新设定 Virtual Table 的指针__vptr
前面曾学到过,C++对象模型是如何支持 virtual 机制的。是通过在 class 内安插了一个 vptr 指向其相应的 vtbl。因此,如果编译器不能正确的初始化 vptr,将导致严重的后果。所以,当一个 class 有 virtual functions 时,就不在展现出 Bitwise semantics。于是编译器将会合成一个 copy constructor
来初始化 vptr。下面是列子。
【注】一个 class 对应一个 vtbl。相同 class 的 objects 的 vtbl 相同,也即 vptr 指向的地址相同。
class Base { public: Base(); virtual ~Base(); // 基类的析构函数一定要 virtual,否则会导致严重后果。 virtual void draw(); }; class Derive : public Base { public: Derive(); void draw(); // 虽没写明 virtual,但实际上是 virtual。 }; Derive yogi; Derive winnie = yogi; // OK! winnie 和 yogi 的 vtbl 地址相同。可以直接拷贝 vptr 的值。 Base franny = yogi; // 注意,franny 的 vptr 和 yogi 的 vptr 指向不同的 vtbl,因此,copy contructor 需要重新设定 franny 的值,防止其指向 Derive 的vtbl. // 这里还涉及到以子类初始化基类时的切割问题 |
处理 Virtual Base Class Subobject
对于虚拟继承(即:继承自一个虚基类),编译器必须维护 派生类中虚基类的位置(要理解这个“位置”的含义,首先你的了解继承后的对象模型是怎样的)。
直接按位拷贝可能会破坏这个"位置"。
class Raccoon : public virtual ZooAnimal { // ... } class RedPanda : public Raccoon { // ... } |
【说明】近期项目上线,工作较忙,更新像蜗牛!后续加快节奏!
相关文章推荐
- 【深度探索C++对象模型】第二章 构造函数语意学(上)
- 《深入探索C++对象模型》第二章:构造函数语意学(上)
- 深度探索C++对象模型 第二章构造函数语意学
- 深度探索C++ 对象模型【第二章1】
- 深度探索C++对象模型复习和学习 第二章:构造函数语义学(The Semantics of Constructors)
- 深入探索C++对象模型 第二章 构造函数语意学
- 深入探索C++对象模型:第二章构造函数语意学
- 深度探索C++对象模型 第二章 读书笔记
- 《深度探索c++ 对象模型》有感之构造函数和析构函数不能调用虚函数
- 深度探索C++ 对象模型【第二章2】
- 深度探索C++对象模型——构造函数
- 深度探索C++对象模型 之 构造函数语意学
- 深度探索C++对象模型 第二章 读书笔记
- 《深入探索C++对象模型》第二章:构造函数语意学(下)
- 深度探索C++对象模型学习 之 C++构造函数语意学(一)
- 深度探索C++对象模型1
- 读【深度探索C++对象模型】【下】
- 读【深度探索C++对象模型】【上】
- 深度探索C++对象模型(4)
- 深度探索C++对象模型学习笔记——Function语意学