原文地址:http://www.coderess.com/?p=55
什么是复制构造?
复制构造是类或结构体的一种特殊的拷贝已存在实例构造函数。根据C++标准,复制构造函数可以有以下几种形式:
MyClass( const MyClass& other ); MyClass( MyClass& other ); MyClass( volatile const MyClass& other ); MyClass( volatile MyClass& other );
注意不要写成以下形式,尽管它们也能够实现同样的事:
MyClass( MyClass* other ); MyClass( const MyClass* other ); MyClass( MyClass other );
什么时候需要写自己的复制构造函数?
首先,你需要明白假如你没声明一个复制构造函数,编译器会给你提供默认的构造函数。这个默认的构造函数能智能地(member-wise)复制源对象,比如这个类:
class MyClass { int x; char c; std::string s; };
编译器提供的默认构造函数相当以下功能:
MyClass::MyClass( const MyClass& other ) : x( other.x ), c( other.c ), s( other.s ) {}
在很多情况下,这是足够的。然而,有些情景下member-wise的拷贝方式是不够的。尤为常见的原因是默认的复制构造会产生野指针(raw pointer),这个时候你需要深复制指针成员,这是当你不是想复制指针本身,而是想复制指针指向的内容。为什么需要深复制?这是因为通常一个实例拥有指针,同时在某些时候它也负责删除这个指针,比如析构函数发生的前一刻。假如两个对象都析构调用 delete 函数删除非空指针成员,会导致堆栈溢出。
使用编译器提供的默认复制构造函数,很少情况不会出现野指针,因此默认构造函数是不足够的。你也可以使用引用计数。比如boost::shared_ptr<>。
正确使用const (Const correctness)
当通过引用的方式传递参数给函数或者构造对象时,要很小心地正确使用const。仅仅当函数会修改传递的函数才使用non-canst引用,否则就应该使用const引用。
为什么强调要这样? 这是一个C++标注的小条款:non-const 引用不能绑定到临时对象,临时对象时一个没有变量名的实例,比如:
std::string( "Hello world" );
是一个临时对象,因为没有变量名。下面一个不是临时对象:
std::string s( "Hello world" );
这个实用的提醒有什么用?请看下面:
// Improperly declared function: parameter should be const reference: void print_me_bad( std::string& s ) { std::cout << s << std::endl; } // Properly declared function: function has no intent to modify s: void print_me_good( const std::string& s ) { std::cout << s << std::endl; } std::string hello( "Hello" ); print_me_bad( hello ); // Compiles ok; hello is not a temporary print_me_bad( std::string( "World" ) ); // Compile error; temporary object print_me_bad( "!" ); // Compile error; compiler wants to construct temporary // std::string from const char* print_me_good( hello ); // Compiles ok print_me_good( std::string( "World" ) ); // Compiles ok print_me_good( "!" ); // Compiles ok
很多STL的容器和算法都要求一个对象时可拷贝的。通常,这意味着你需要有实用const传引用的复制构造函数。 (最近一个搞了很久的错误就是这个问题,所以觉得这篇文章很好,就翻译过来分享了)
插曲(译者经历):
我在使用list的时候,用到了replace泛型算法:
template < class ForwardIterator, class T > void replace ( ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value )
{
for (; first != last; ++first)
if (*first == old_value) *first=new_value;
}
很明显这个算法要求对象具有 == 操作符来比较。我也为对象重载了 == 操作符: