class X { ... };
X x;
// 明确地以一个object的内容作为另一个class object的初值
X xx = x; 另两种情况是当object被当作参数交给某个函数时,例如extern void foo(X x);
void bar() {
X xx;
// 以xx作为foo()第一个参数的初值(隐式的初始化操作)
foo(xx);
} 以及当函数传回一个 class object时。例如:X foo_bar() {
X xx;
return xx;
} 假设 class 设计者明确定义了一个copy constructor(这是一个constructor,有一个参数的类型是其 class type),例如:// user-defined copy constructor的实例 // 可以是多参数形式,其第二个参数及后继参数以一个默认值供应之 X::X(const X &x); Y::Y(const Y &y, int = 0);那么在大部分情况下,当一个 class object以另一个同类实体作为初值时,上述的constructor会被调用。这可能会导致一个暂时性 class object的产生或程序代码的蜕变。
class String {
public:
// ... 没有 explicit copy constructor
private:
char *str;
int len;
};
一个String object的default memberwise initialization发生在这种情况下:String noun("book");
String verb = noun;
其完成方式就像个别设定每一个members一样:
verb.str = noun.str;
verb.len = noun.len;
如果一个String object被声明为另一个 class 的member,如下所示:class Word {
public:
// ... 没有 explicit copy constructor
private:
int _occurs;
String _word; // String object成为class Word的一个member
}; 那么一个Word object的default memberwise initialization会拷贝其内建的member _occurs,然后再从String member object _word递归实施memberwise initialization。#include "Word.h"
Word noun("book");
void foo() {
Word verb = noun;
} 很明显verb是根据noun来初始化,但在尚未看到 class Word声明之前,不可能预测这个初始化操作的程序行为,如果 class Word的设计者定义了一个copy constructor,verb的初始化操作会调用它,但如果该 class 没有定义explicit copy constructor,那么是否会有一个编译器合成的实体被调用呢?这就视该 class 是否展现"bitwise copy semantics"而定。如下所示:// 以下声明展现了bit copy semantics
class Word {
public:
Word(const char *);
~Word() {
delete []str;
}
private:
int cnt;
char *str;
}; 这种情况下并不需要合成出一个default copy constructor,因为上述声明展现了"default copy semantics",因此verb的初始化操作就不需要一个函数调用,然而,如果 class Word是这样声明的:// 以下声明并未展现出bitwise copy semantics
class Word {
public:
Word( const String &);
~Word();
private:
int cnt;
String str;
};
其中String声明了一个explicit copy constructor:
class String {
public:
String(const char *);
String(const String &);
~String();
}; 在这种情况下,编译器必须合成出一个copy constructor以便调用member class String object的copy constructor:// 一个被合成出来的copy constructor
// C++伪代码
inline Word::Word(const Word &wd) {
str.String::String(wd.str);
cnt = wd.cnt;
} 有一点值得注意:在这被合成出来的copy constructor中,如整数、指针、数组等等的nonclass members也都会被复制。class ZooAnimal {
public:
ZooAnimal();
virtual ~ZooAnimal();
virtual void animate();
virtual void draw();
private:
// ZooAnimal的animate()和draw()
// 所需要的数据
};
class Bear : public ZooAnimal {
public:
Bear();
void animate();
void draw();
virtual void dance();
private:
// Bear的animate()和draw()和dance()
// 所需要的数据
};
ZooAnimal class object以另一个ZooAnimal class object作为初值,或Bear class object以另一个Bear class object作为初值,都可以直接靠"bitwise copy semantics"完成。例如:Bear yogi; Bear winnie = yogi;yogi会被 default Bear constructor初始化,而在constructor中,yogi的vtpr被设定指向Bear class 的 virtual table。因此,把yogi的vptr值拷贝给winnie的vptr是完全的。
ZooAnimal franny = yogi; // 这会发生切割(sliced)franny的vptr不可以被设定指向Bear class 的virtual table,否则当下面程序片段中的draw()被调用而franny被传进去时,就会"炸毁"(blow up)
void draw (const ZooAnimal &zoey) {
zoey.draw();
}
void foo() {
// franny的vptr指向ZooAnimal的virtual table
// 而非Bear的virtual table
ZooAniaml franny = yogi;
draw(yogi); //调用Bear::draw()
draw(franny); //调用ZooAnimal::draw()
} 通过franny调用virtual function draw(),调用的是ZooAnimal实体而非Bear实体(虽然franny是以Bear object yogi作为初始值)。因为franny是一个ZooAnimal object。事实上,yogi中的Bear部分已经在franny初始化时被切割(sliced)。如果franny被声明为一个reference(或者如果它是一个指针,而其值为yogi的地址),那么经由franny所调用的draw()才会是Bear的函数实体。class Raccon : public virtual ZooAnimal {
public:
Raccon(){ /* 设定private data初值 */ }
Racccon(int val) { /* 设定private data初值 */ }
// ...
private:
// 所需要的数据
}; 编译器所产生的代码(用以调用ZooAnimal的default constructor,将Racccon的vptr初始化,并定位出Raccon中的ZooAnimal subject)被插入在两个Raccon constructors之间。class RedPanda : public Raccon {
public:
RedPanda() { /* 设定private data初值 */ }
RedPanda(int val) { /*设定private data初值 */ }
private:
// ...
}; 如果以一个Reccon object作为另一个Raccon object的初值,那么"bitwise copy"就戳戳有余了// 简单的bitwise copy就足够 Raccon rocky; Raccon little_critter = rocky;然而如果企图以一个RedPanda object作为little_critter的初值,编译器必须判断"后续当程序员企图存取其ZooAnimal subobject时是否能够正确地执行"
// 简单的bitwise copy还不够 // 编译器必须明确地将litte_critter的virtual base class pointer/offset初始化 RedPanda little_red; Raccon little_critter = little_red;在这种情况下,为了完成正确的little_critter初值设定,编译器必须合成一个copy constructor,插入一些码以设定 virtual base class pointer/offset的初值,对每一个members执行必要的memberwise初值化操作,以及执行其它的内存相关操作(3.4对于 virtual base classes有更详细的讨论)
// 简单的bitwise copy可能够用,可能不够用 Raccon *ptr; Raccon little_critter = *ptr;当一个初始化操作存在并保持着"bitwise copy semantics"的状态时,如果编译器能够保证object有正确而相等的初始化操作,是否它应该抑制copy constructor的调用,以使其所产生的程序代码优化?
版权声明:本文为博主原创文章,未经博主允许不得转载。
C++对象模型——Copy Constructor 的建构操作(第二章)
原文:http://blog.csdn.net/yiranant/article/details/47158441