大多数类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符、无论是隐式还是显式。
但对于某些类来说这些操作并无意义,在此种情况下,定义时必须采用某种机制阻止赋值或拷贝。
(1)定义删除的函数
在函数的参数列表后面加上=delete来指明希望将其定义为删除的。
struct NoCopy {
NoCopy()=default; //使用合成的默认构造函数
NoCopy(const NoCopy&)=delete; //阻止拷贝
NoCopy &operator=(const NoCopy &)=delete; //阻止赋值
~NoCopy()=default; //使用合成的析构函数
};
=delete 必须出现函数第一次声明的时候,这和=default不同
与=default 的另一个不同之处是,我们可以对任何函数指定=delete(我们只对编译器可以合成的默认构造函数和拷贝控制成员使用=default)。
析构函数不能是删除的成员
我们不能删除析构函数。对于删除了析构函数的类型,我们不能定义该类型的变量或成员,但可以动态分配这种类型的对象。但是这些对象不可以释放。
struct NoDtor{
NoDtor()=default;
~NoDtor()=delete;
};
NoDtor nd; //error:析构函数被删除
NoDtor *p=new NoDtor(); //正确:但不可delete p;
delete p;
合成的拷贝控制成员可能是删除的
如果一个类有数据成员不能默认构造、拷贝、复制或销毁,则对应的成员函数将被定义为删除。
对于具有引用成员或无法默认构造的const成员的类,编译器不会为他们合成默认构造函数
如果一个类有const成员,则它不能使用合成的拷贝赋值运算符。毕竟,此运算符试图赋值所有成员,而将一个新值赋予一个const对象是不可能的。
对于有引用成员的类,合成构造赋值运算符被定义为删除的。
(2)private拷贝控制
class PrivateCopy{
/*拷贝控制成员为private,则普通用户不可访问*/
PrivateCopy(PrivateCopy &);
PrivateCopy &operator=(PrivateCopy &);
public:
PrivateCopy()=default;
~PrivateCopy();
};
友元和成员函数仍可拷贝对象,为了阻止其拷贝,我们将这些拷贝声明为private的,但并不定义他们。
C++ 11标准,希望使用=delete来阻止拷贝,而非声明为private的
行为像值的类
类的行为像一个值,有着自己的状态,对于拷贝一个像值的对象时,副本和原对象是独立的,改变副本的值不会改变原对象的值。
类值的行为,对于类管理的资源,每个对象都应有一份自己的拷贝。ex
定义一个HasPtr类,让他的行为像一个值
(1)定义一个拷贝构造函数,完成string的拷贝
(2)定义析构函数来释放string
(3)定义一个拷贝赋值构造函数来释放当前的string,并从右侧对象拷贝string
class HasPtr{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s)),i(0) {}
//对ps指向的string,每个HasPtr对象都有自己的拷贝
HasPtr(const HasPtr &p):ps(new std::string(*p.ps)),i(p.i){}
HasPtr & operator=(const HasPtr &);
~HasPtr(){delete ps;}
private:
std::string *ps;
int i;
};
HasPtr & HasPtr::operator=(const HasPtr &rhs)
{
auto newp=new string(*rhs.ps); //拷贝底层string
delete ps; //释放旧内存
ps=newp; //从右侧运算对象拷贝数据到本对象
i=rhs.i;
return *this; //返回本对象
}
定义行为像指针的类
行为像一个指针的类共享状态,当我们拷贝一个像指针的对象时,原对象和副本是共享底层数据,改变副本也会改变原对象。
引入计数机制,进行直接管理资源,最好的方法当然是使用share_ptr来进行实现类似行为指针的类。
为类定义拷贝构造函数和拷贝赋值函数,来拷贝指针成员本身而非它指向的string
class HasPtr{
public:
//构造函数分配新的string和计数器,并将计数器置为1。
HasPtr(const std::string &=std::string()):ps(new string(s),i(0),use(new std::size_t(1)){}
//拷贝构造函数拷贝所有的三个数据成员,并递增计数器。
HasPtr(const HasPtr &p):ps(p.ps),i(p.i),use(p.use){++*use;}
HasPtr & operator=(const HasPtr&);
~HasPtr();
private:
std::string *ps;
int i;
std::size_t *use;
};
HasPtr& HasPtr::operator=(const HasPtr & rhs)
{
++*rhs.use;
//旧内存的释放
if(--*use==0)
{
delete ps;
delete use;
}
ps=rhs.ps;
i=rhs.i;
use=rhs.use;
return *this;
}
HasPtr::~HasPtr()
{
if(--*use==0){
delete ps;
delete use;
}
}