类中只要有一个成员不可能拷贝、赋值、析构的话,类的合成拷贝控制成员就被定义为delete
在新标准发布之前,要想阻止拷贝,类将拷贝构造函数和拷贝赋值运算符声明为private,只是声明,没有给出定义。
这样一来,试图拷贝对象的用户代码在编译阶段就会被标记为错误。成员函数或者友元函数中的拷贝操作将会导致链接时错误。
定义行为像值的类
当我们拷贝一个像值的对象时,副本和原对象是完全独立的。改变副本不会对原对象有任何影响。
using namespace std;
class has
{
string* str;
int i;
public:
has(const string& s = string()) :str(new string (s)), i(0) {}
has(const has & h) :str(new string(*h.str)), i(h.i) {}
has& operator=(const has &);
~has() { delete str; }
};
has& has::operator=(const has& hs)
{
string* tmp = new string(*hs.str);
delete str;
str = tmp;
i = hs.i;
return *this;
}
当我们编写赋值运算符的时候,有两点需要记住:
1)如果将一个对象赋值给对象本身的话,赋值运算符必须能够正确工作;
2)大多数的赋值运算符组合了拷贝构造函数和析构函数的工作。
当你在编写一个赋值运算符的时候,一个好的模式是:
先将等号右侧的对象拷贝到一个临时的对象中。当拷贝完成之后,销毁左侧的对象的现有成员就是安全的了。一旦左侧的运算对象的资源被销毁的话,就只剩下将临时对象的资源拷贝到左侧的运算对象的成员之中。
定义行为像指针的类
采用计数器机制,对象的拷贝不会改变底层的结构,也就是说。当一个对象,另一个对象随之改变。
using namespace std;
class has
{
string* str;
int i;
size_t* use;
public:
has(const string & s = string()) :str(new string(s)), i(0), use(new size_t(1)) {}
has(const has& hs) :str(hs.str), i(hs.i), use(hs.use) {}
has& operator=(const has& h);
~has();
};
has& has::operator=(const has &h)
{
++*h.use;
if (--*use == 0)
{
delete str;
delete use;
}
str = h.str;
i = h.i;
use = h.use;
return *this;
}
has::~has()
{
if (--*use == 0)
{
delete str;
delete use;
}
}