问题:看了下面的代码,你认为会输出什么结果呢?
class PoorCharlie {
public:
PoorCharlie(const string& name) : m_Name(new string(name))
{}
~PoorCharlie() { delete m_Name; }
const string* getName() { return m_Name; }
private:
string* m_Name;
};
static void somefun(PoorCharlie poor)
{
poor.getName();
}
int main(int argc, char* argv[])
{
PoorCharlie foo("Foo");
somefun(foo);
return 0;
}
答案是: Crash!
问题是如果这个foo被当作参数传递时,可能产生临时变量调用到了编译器默认生产的拷贝构造,这个构造函数是浅拷贝,导致poor和foo中的m_Name指向了同一个内存。当两个实例析构的时候,m_Name被delete了两次导致crash。
C++编译器会为类生成默认的四个函数:默认构造函数、析构函数、拷贝构造函数、赋值函数。一不小心,可能就会让类的使用者掉入坑中。所以在设计这个类时候就需要预防这种莫名其妙的陷阱。
那么如何做呢?可以把拷贝构造函数和赋值函数声明成private或者delete。这样,可以避免无意调用产生错误。如:
class BetterCharlie {
public:
BetterCharlie(const string& name) : m_Name(new string(name))
{}
~BetterCharlie() { delete m_Name; }
const string* getName() { return m_Name; }
private:
// Declare the copy ctor/assign operator delete to avoid unexpected call
BetterCharlie(const BetterCharlie& other) = delete;
BetterCharlie& operator=(const BetterCharlie& other) = delete;
string* m_Name;
};
static void somefun(BetterCharlie better)
{
better.getName();
}
int main(int argc, char* argv[])
{
BetterCharlie foo("Foo");
somefun(foo);
return 0;
}
编译器会自动报错。