我们都知道,C++语言的机制会背地里为我们做一些事情。但是有时候我们是不需要有些默认的操作。我们并不想是使用编译器为我们产生的函数,也不想让使用我们所写代码的人使用这些函数。那么,我们应该明确的拒绝(比如我们想禁止某些对象的复制时)。
假设我们想设计一套用于服务学校的缴费系统,那么其中就有学生类:
classCStudent{
public:
CStudent(std::string name ="",int age =0,int id =0,int nfund =0);
CStudent(constCStudent& other);
~CStudent();
CStudentoperator=(constCStudent& other);
private:
std::stringm_sName;
intm_nAge;
intm_nStudentID;
intm_nFund;
};
因为每一个学生都是独一无二的,所以在学生类中根本就不需要复制对象操作。因而上述类声明
的拷贝构造函数和赋值函数都变得毫无意义了。那么我们该怎么避免拷贝构造函数和赋值函数呢?
第一种方法:
通过注释的方式告诉代码使用者:“不能使用拷贝构造函数和赋值函数”。这里虽然是一种方法,但是不能达到“禁止”的目的。
所以最好的方法是在编译的时候就拒绝你的拷贝和复制操作。通常而言,如果你不希望一个类支持某种操作,最简单的方法是什么也不做,因为编译器会对你使用根本没有声明过的函数给出错误提醒。但是对于类的复制操作运算却无能为力,因为编译器不能一方面为你悄悄的声明它们,另一方面却又告诉你它们的声明是无效的,所以这里就产生了矛盾。
那我们应该怎么做来解决这一棘手的问题呢?类的访问控制(Access Control)为我们指明了前进的方向。所有的编译器生成的函数都是public的,如果我们将它们声明为private,一方面阻止了编译器为我们默认声明与定义,另一方面又能禁止对复制操作函数的显示调用。
第二种方法:声明复制操作函数的访问属性为private
classCStudent{
public:
CStudent(std::string name ="",int age =0,int id =0,int nfund =0);
~CStudent();
private:
CStudent(constCStudent& other);
CStudentoperator=(constCStudent& other);
private:
std::stringm_sName;
intm_nAge;
intm_nStudentID;
intm_nFund;
};
但是这种解决方法还有一点缺陷,如果成员函数和友元函数要对其访问,仍然是畅通无阻的。因为C++的语言机制赋予了它们访问调用private成员函数的权利。不过我们进一步想到了,假如我们只声明不定义,那么成员函数、友元函数对它们的调用就会被编译器明令禁止:“声明而不定义成员函数是合法的,但是使用未定义成员函数的任何行为将导致链接失败!”。这样就实现了类复制操作的完全禁止。
但是,但是!有没有更好的方法呢?上述方法是在链接的时候提示错误,我们是否可以进一步把链接时期错误,提前到编译时期呢?答案是:可以的!
第三种方法:继承自一个禁止复制操作的基类
namespacenoncopyable
{
classnoncopyable{
protected:
noncopyable(){}
~noncopyable(){}
private:
noncopyable(constnoncopyable&);
constnoncopyable&operator=(constnoncopyable&);
};
}
classCStudent:publicnoncopyable::noncopyable{
public:
CStudent(){}
~CStudent(){}
};
上面我们不需要对学生类声明private的拷贝构造函数和赋值操作符,而是特意声明了一个不可复制的类noncopyable(该类参照boost::noncopyable)。这个基类非常简单,就是把想要禁止的函数声明的访问控制属性为private。
为了禁止学生类的拷贝,只要让学生类private的继承noncopyable基类即可。
当调用学生类的拷贝构造函数或者赋值操作符时,编译器使用生成一个拷贝构造函数和一个拷贝赋值操作符,而它调用这些函数时不可避免的调用基类的对应函数,由于在基类中这些操作是 private 的,因此编译器会在编译时期报错。