本篇的目标是要学会一个条款:
为了驳回编译器自动(暗自)提供的机能(函数),你可以将相应的成员函数声明为private并且不予以实现。
下面不妨我们先来看一段代码:
class HomeForSale
{
...
};
void test()
{
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h2);//企图拷贝h2 给 h3
h1 = h2;//企图拷贝 h2 给h1
}
这里假定我们的HomeForSale类的对象被认为是独一无二的,那么我们j就不希望这几条拷贝的语句给编译器通过!
但我们知道,usually,如果你不希望你的class支持or实现某些特定的功能的话,你不声明对应的函数即可。可这个策略对于 copy 构造函数 以及重载的copy assignment 运算符函数而言是无用的。因为在条款5中我们已经指出,if你不去声明它们,那么编译器就会在他们被需要(被调用)的时候自动生成,这似乎把你逼近一个困境,也即if你不去声明 copy 构造函数 以及重载的copy assignment 运算符函数,那么编译器会为你自动产出一份,于是你的class就会支持copying了。但如果你声明它们,那么你的类还是支持copying。但我们这里的目标却是阻止copying!
通过以上的简单讨论,你是不是很想解决这个问题呢?
于是,在Effective C++ 的条款6中,给我们提供了解决方案
:将成员函数声明为private并且故意不实现它们
下面让我们将这一伎俩施加于HomeForSale类:
class HomeForSale
{
public:
//...
HomeForSale() {}
private:
//...
HomeForSale(const HomeForSale& );
HomeForSale& operator=(const HomeForSale& );//只有声明
//注意:
//这里因为只有函数的声明,因此我并不一定要把参数名称写出来,因为我不定义的话,写出来又有何用呢?
//当然,你定义的时候参数列表的参数名称必须要写出来!
};
void test()
{
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h2);//报错!企图拷贝h2 给 h3 是不行的,因为在类外不能访问类内的private下的成员
h1 = h2;//报错!企图拷贝 h2 给h1 也是不行的,因为在类外不能访问类内的private下的成员
}
通过以上代码,将copy 构造函数 以及重载的copy assignment 运算符函数 定义在一个基类(父类)中,并把作用域设置为private,可以解决我们的目标问题的一部分。
但你不妨想一想,有什么语法形式是可以访问到类内的private成员的呢?
对的,member函数 以及 friend友元函数 可以访问对于类内的private成员。
因此我们说这半个伎俩还不是绝对安全的,因此下面我们要介绍另一半伎俩
把想阻止的 copy 构造函数 以及重载的copy assignment 运算符函数 定义在一个基类(父类)中,并把属性设置为private,然后再让一个类去继承这个不可以进行以上操作的基类,这样用这个类所创建的对象都可以达成uncopy的目的了。
且往下看:
class Uncopyable
{
protected:
//这个默认的构造和析构函数是为了 允许 子类(派生类)的对象的构造和析构的
Uncopyable(){}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);//但阻止子类(派生类)对象的copying
};
class HomeForSale:public Uncopyable
{
public:
friend void copyHomeForSaleFun();
//...
//HomeForSale() {}
private:
//...
//HomeForSale(const HomeForSale& );
//HomeForSale& operator=(const HomeForSale& );//只有声明
//注意:
//这里因为只有函数的声明,因此我并不一定要把参数名称写出来,因为我不定义的话,写出来又有何用呢?
//当然,你定义的时候参数列表的参数名称必须要写出来!
};
void copyHomeForSaleFun()
{
cout << "这是HomeForSale的friend友元函数被调用了!" << endl;
HomeForSale hh1,hh2,hh3;
hh1 = hh2;//报错!
//因为当HomeForSale的对象试图拷贝时 都必须要先调用父类(基类)的构造函数,
//又因为父类的上述函数都是private的,因此你即便是友元函数也无法访问起父类的"隐私"private成员!
HomeForSale hh3(hh1);//报错!理由同上!
}
void test()
{
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h2);//报错!企图拷贝h2 给 h3 是不行的,因为在类外不能访问类内的private下的成员
h1 = h2;//报错!企图拷贝 h2 给h1 也是不行的,因为在类外不能访问类内的private下的成员
}
由于继承后,子类的对象在进行创建和拷贝赋值操作时都必须要先调用父类(基类)的上述函数,基于此,friend友元函数也无法访问父类的"隐私"的private成员!
(当然,我不论你这里是何种继承方式,继承后的类中的成员都是无法访问其父类的"隐私"private成员的!)
即便是member成员函数,也会报错!理由与上述代码的报错无差!
就比如在类中添加一个member函数:
void MyFunc() {
cout << "这是member函数的调用!" << endl;
HomeForSale hhh1, hhh2;
hhh2 = hhh1;
HomeForSale hhh3(hhh1);//报错!
//因为当HomeForSale的对象试图拷贝时 都必须要先调用父类(基类)的构造函数,
//又因为父类的上述函数都是private的,因此你即便是member函数也无法访问起父类的"隐私"private成员!
}
综上,我相信你已经理解并学会使用这个条款了。
总结:
为了驳回编译器自动(暗自)提供的功能(函数),你可以将相应的实现这些功能的成员函数声明为private并且不予以实现。若为了更加安全的达成这一目标,我们会使用一个像Uncopyable这样的base class!
参考:
Effective C++ 之条款06