4.Copy Constructor的构造操作

目录

1、对象赋值问题引入

2、Bitwise Copy Semantics(位逐次拷贝)

3、处理class virtual function

4、处理virtual base class subobject


1、对象赋值问题引入

在C++中,有三种情况会以一个object的内容作为另一个class object的初值。这三种情况分别如下:

第一种,显式地以一个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;
}

那么问题来了,以一个object的内容作为另一个class object的初值,C++语言是怎么进行操作的

假设class设计者显示定义了一个copy constructor,像这个样子。

//user-defined copy constructor 的实例
//可以是多参数的形式,其第二参数以及后继参数以一个默认值供应之
X::X(const X &x);
Y::Y(const Y & y,int = 0);

在大部分的情况下,以上的问题将会调用copy constructor。少数情况会执行Bitwise Copy。

如果class没有提供一个explicit copy constructor,当class object以相同的class的另外一个object作为初值,其内部以default memberwise initialization的方法完成。也就是每一个内建的或者派生的data member的值,从一个object拷贝到另外的一个object上。不过它不会拷贝其中的member class object,而是以递归的方式施加memberwise initialization。例如,有一个下面的class声明:

class String
{
public:
	//...没有explicit copy constructor
private:
	char *str;
	int len;
};

对于String verb = noun;语句,他的完成方式像是设定每一个members一样:

//语意相等
verb.str = noun.str;
verb.len = noun.len;

以上是一种memberwise initialization。如果String object是另外一个class的member,像这样子:

class Word
{
public:
	//...没有explicit copy constructor
private:
	int _occurs;
	String _world;
};

对于Word verb = noun;语句,他的完成方式像是设定每一个members一样:

//语意相等
verb.occurs = noun.occurs;//bitwise copy
verb._world = String::String(noun._world);//编译器生成copy constructor,并执行拷贝构造函数

Default constructors和copy constructors在必要的时候才由编译器产生出来。这里“必要的时候”指的是当class不展示bitwise copy semantics时候。

2、Bitwise Copy Semantics(位逐次拷贝)

一个class什么时候不展示“bitwise copy semantics”?有下面的4种情况:

1)当一个class内含一个member object而后者的class声明一个copy constructor时(不论是class设计者显示的声明,还是编译器隐式的合成),例如下面的代码,String显示的声明了,而Word隐式的合成。

class String
{
public:
	String(const char *);
	String(const String &);
	~String();
	//...
};
class Word
{
public:
	Word(const String &);
	~Word();
	//...
private:
	int cnt;
	String str;
};

2)当class继承一个base class而后者存在一个copy constructor时(不论是显示声明还是隐式的被合成)。例如class Dog没有拷贝构造函数,当发生一个Dog object的内容作为另一个Dog object的初值,Dog会被编译器合成一个copy constructor。

class Animal
{
public:
	Animal(const char *);
	Animal(const Animal &);
	~Animal();
	//...
};
class Dog:public Animal
{
public:
	Dog(char * name);
	~Dog();
};

3)当class声明一个或者多个virtual function时。

class Student
{
public:
	Student();
	~Student();
	
	String virtual get_name();
};

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

class Postgraduate : virtual public Student
{
public:
	Postgraduate();
	~Postgraduate();
	
	String get_Thesis_topic();
};

3、处理class virtual function

在前一节的bitwise copy semantics,提到当class声明一个或者多个virtual function时会被编译器合成一个copy constructor。此外class会进行两种扩张操作:

  • 增加一个virtual function table(vtbl),内含每一个有作用的virtual function的地址。
  • 一个指向virtual function table的指针(vtpr),安插在每一个class object内部。

首先,我定义两个class,ZooAnimal和Bear:

class ZooAnimal
{
public:
	ZooAnimal();
	virtual ~ZooAnimal();
	
	virtual void animate();
	virtual void draw();
	//...
private:
	//ZooAnimal 的animate() 和 draw()
	//所需的数据
};
class Bear : public ZooAnimal
{
public:
	Bear();
	void animate();//译注:虽未写明是virtual,但它其实是virtual
	void draw();//译注:虽未写明是virtual,但它其实是virtual
	virtual void dance();
	//...
private:
	//Bear 的animate() 、 draw() 和 dance()
	//所需的数据	
};

然后,Bear class object以另一个Bear class object作为初值,我可以看到yogi的vptr值拷贝给了winnie的vptr了,这样的操作是安全的,它是通过编译器合成的拷贝构造函数完成的。

最后,当一个base class object以其derived class的object内容做初始化操作时,其vptr复制操作也必须保证安全,例如:

ZooAnimal franny = yogi; //译注:派生类给基类赋值

4、处理virtual base class subobject

Virtual base class 的存在需要特别的处理。一个class object如果以另一个object作为初值,而后者有一个virtual base class subobject,那么也会使“bitwise copy semantics”失效。看看如下继承关系的代码。

class ZooAnimal
{
public:
	ZooAnimal();
	virtual ~ZooAnimal();
	
	virtual void animate();
	virtual void draw();
	//...
private:
	//ZooAnimal 的animate() 和 draw()
	//所需的数据
};

class Raccon : public virtual ZooAnimal
{
public:
	Raccon(){/*设定private data的初值*/}
	Raccon(int val){/*设定private data的初值*/}
	//...
private:
	//所有必要的数据
};

class RedPanda : public virtual Raccon
{
public:
	RedPanda(){/*设定private data的初值*/}
	RedPanda(int val){/*设定private data的初值*/}
	//...
private:
	//所有必要的数据
};

对于如下的代码,little_red和little_critter的关系如下图所示。

RedPanda little_red;
Raccon little_critter = little_red;

在这种情况下,为了完成正确的设定little_critter初值设定,编译器必须合成一个copy constructor,安插一些代码以设定virtual base class pointer/offset的初值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值