对象复制语意学(Object Copy Semantics)

本文探讨了如何在C++中实现Point类及其派生类Point3d的浅拷贝和深拷贝操作,特别是关注了虚拟继承带来的问题,如合成的copyassignmentoperator的不足和正确调用基类及子类成员的技巧。作者建议避免虚拟基类的拷贝,以确保语义正确性。
摘要由CSDN通过智能技术生成

当我们设计一个class,并以一个class object赋值给另一个class object时,我们就用到了拷贝复制操作,我们可以选择默认的浅拷贝,也可以自己提供一个explicit copy assignment operator,或者显示的拒绝把一个class object赋值给另一个class object(将copy assignment operator声明为private)。

class Point
{
public:
	Point(float x = 0.0,float y = 0.0,float z = 0.0);
	//...(没有virtual function)
protected:
	float _x,_y;
};

对于上述代码,如果要支持一个简单的拷贝操作,那么默认的行为不但足够而且有效率,只有在默认行为所导致语义不安全或不正确的情况下,才需要设计一个copy assignment operator。

由于此class已经有bitwise copy语意,所以implicit copy assignment operator和copy constructor一样被视为毫无用处,也根本不会合成出来。

C++ Standard上说copy assignment operators并不表示bitwise copy semantics是nontrivial,只有nontrivial 才会被合成出来,以下情况不会表现bitwise copy语意:

①当class内含一个member object,而其class有一个copy assignment operator时。
②当一个class 的base class有一个copy assignment operator 时。
③当一个class声明了任何virtual function(我们一定不要拷贝右端class object的vptr地址,因为它可能是一个derived class object)。
④当class 继承自一个virtual base class(不论此时base class有没有copy operator)时。

对于Point class,这样的赋值操作:

Point a,b;
...
a = b;

由于是bitwise copy完成,把Point b拷贝到Point a,期间没有copy assignment operator被调用;我们还可以提供一个copy constructor,把NRV优化打开,copy constructor出现不应该让我们也一定提供一个copy assignment operator。

现在导入一个copy assignment operator,来说明该operator在继承下的行为:

inline Point&
Point::operator = (const Point& rhs)
{
	_x = rhs._x;
	_y = rhs._y;
	return *this;
}

现在派生一个Point3d class(虚拟继承):

Point3d : virtual public Point
{
public:
	Point3d(float x = 0.0,float y = 0.0,float z = 0.0);
	//...
protected:
	float _z;
};

如果我们没有为Point3d定义一个copy assignment operator,编译器就必须合成一个(因为前述第二项和第四项),合成的看起来像如下:

//C++ pseudo
//被合成的copy assignment operator
inline Point3d&
Point3d::operator = (Point3d* const this,const Point3d& p)
{
	//调用base class的拷贝赋值函数
	this->Point::operator(p);
	
	//memberwise copy the derived class members 
	_z = p.z;
	return *this;
}

copy assignment operator有一个不够理想、不够严谨的情况,就是它缺乏一个member assignment list(不能像构造函数那样member initialization list),因为不能写:

//C++ pseudo,以下性质不支持
inline Point3d&
Point3d::operator = (const Point3d& p3d)
	:Point(p3d),z(p3d._z)
{}
 
//必须写成以下的两种形式,
//才能调用base class 的copy assignment operator
Point::operator = (p3d);
//或者
(*(Point)this) = p3d;

缺少copy assignment list,编译器就无法压抑上一层base class的copy operators被调用:

//class Vertex: virtual public Point
inline Vertex&
Vertex::operator = (const Vertex& v)
{
	this->Point::operator(v);
	_next = v._next;
	return *this;
}
 
//从Point3d和Vertex中派生出Vertex3d
inline Vertex3d&
Vertex3d::operator = (const Vertex3d& v)
{
	this->Point::operator(v);
	this->Point3d::operator(v);
	this->Vertex::operator(v);
	//...
}

事实上,copy assignment operator在虚拟继承情况下行为不佳,需要小心的设计和说明。许多编译器甚至并不尝试取得正确语意,它们在每一个中间的copy assignment operator中调用每一个base class instance,于是造成virtual base class copy assignment operator的多个实例被调用。

有一种方法可保证most_derived class会引发virtual base class subobject的copy行为,就是在derived class的copy assignment oper函数实例的最后,显示调用那个operator:

inline Vertex3d&
Vertex3d::operator = (const Vertex3d& v)
{
	this->Point3d::operator(v);
	this->Vertex::operator(v);
	//must place this last if your
	//compiler does not suppress
	//intermediate class invocations
	this->Point::operator(v);
	/...
}

这并不能省略subobjects的多重拷贝,但却可以保证语意正确。另一个方法就是把virtual subobject拷贝到一个分离的函数中,并根据call path条件化的调用它。

作者的建议是不要允许virtual baes class的拷贝操作,甚至不要再任何virtual base class中声明数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值