C++中对象的拷贝一般使用拷贝构造函数,从而对象的拷贝大多是隐式的,使用拷贝构造函数的隐式拷贝很方便,但是编译器无法识别不必要的拷贝,虽然我们人类可以识别这些不必要的拷贝,比如在写函数原型时,忘了加&,就会引发一个这样的非必要拷贝。
如果这种情况很严重,我们可以禁用拷贝构造函数和赋值函数(声明成private),然后再提供一个显式拷贝函数,如:
class HeavyObject {
HeavyObject(const HeavyObject&);
HeavyObject& operator=(const HeavyObject&);
public:
void clone_to(HeavyObject& dest) const;
// more...
};
这种方法的确可以work,但看上去很不自然,比如:
HeavyObject x, y;
// ...
y.clone_to(x); // copy y to x
// 其实我们更习惯这样的表达:
x = y.clone();
x = y.clone(); 在形式上很自然,但是实现却不容易(在C++11中实现要容易点,让 x 接受 && 即可)。
但的确可以实现:在 C++98 标准中,如果一个函数返回一个非 Primitive 的值对象,那么这个对象不能被 bind 到非 const 引用上, 但是可以被修改!
我们可以利用标准的这个特点:
template<class T> struct Ref2 : T {};
template<class T> struct Ref1 {
// 作为返回值对象,Ref1 可以被修改,但不能绑定到 Ref1& 上
T val;
Ref1(const T& y) : val(y) {}
operator Ref2<T>&() // this is a non-const member function
{ return static_cast<Ref2<T>&>(val); }
};
class HeavyObject {
friend struct Ref1<HeavyObject>; // for Ref1 accessing the private copy-cons
HeavyObject(const HeavyObject&);
HeavyObject& operator=(const HeavyObject&);
public:
HeavyObject() {}
void swap(HeavyObject& y) { /*non-throw*/ }
void operator=(Ref2<HeavyObject>& y) {
// prohibit chained assign
this->swap(y); // y is the object created by clone
}
Ref1<HeavyObject> clone() const {
// copy-cons is private and has implementation
// assume return value optimization is enabled
return Ref1<HeavyObject>(*this);
}
// more...
};
当执行 x = y.clone() 时,clone 调用Ref1(const HeavyObject& y) 创建一个临时对象并按值返回,该对象不能 bind 到 non-const reference,但可以被修改!
接下来该对象被传给 HeavyObject::operator=,但该 operator= 只接受 Ref2& 或 const HeavyObject&,于是,编译器需要调用一个 user defined type conversion, 在这里,就是Ref1::operator Ref2&,该函数是 non-const,可以被调用(记得前面说的:不能 bind 到 non-const reference,但可以被修改,从而就可以调用 non-const member function)……
于是,接下来的事情就很简单了……