了解C++默默编写并调用哪些函数
当你创建一个类,但是如果你自己没声明其中一些东西,编译器就会为它声明(编译器版本的)一个拷贝构造函数、一个 copy 操作符和一个析构函数。此外如果你没有声明任何构造函数,编译器也会为你声明一个 默认构造函数。所有这些函数都是 public且 inline。
例:当你写下class Empty{ };
就相当于你写下了:
Class Empty
{
public:
Empty() {...} // 默认构造函数
Empty(const Empty& rhs){...} //拷贝构造函数
~Empty(){...} //析构函数
Empty& operator=(const Empty& rhs){...} // copy 操作符.
};
当这些函数被调用的时候,它们才会被编译器创建出来,如下代码可以使上述每个函数均生成;
Empty e1; //默认构造函数(销毁调用析构)
Empty e2(e1); //拷贝构造
e2 = e1; //拷贝构造符
如果你的类中写了构造函数,但是没有拷贝构造等,编译器会自动给你创建拷贝构造等,但一般来说只有代码合法且可以证明其有意义的才会被生成。万一这两个条件中有一个不符合,则编译器会拒绝生成operator=;
例:成员变量是一个引用
template<class T>
class Name
{
public:
Name(std: strings name, const T& value); //并未声明 operator
private:
string& namevalue;
const T objectvalue;
};
现在考虑下面会发生什么事?
string new("new");
string old("old");
Name<int>p(new,2);
Name<int>s(old,3);
p = s;
刚开始p.name和s.name都指向各自的string对象,当p=s之后,p.name是指向s.name的那个string吗?
当然不是,因为C++不允许让引用改指向不同的对象。所以最好的方法就是自己定义。
如何拒绝让编译器自动生成
假设有有一个叶子类:class Leaf {...}
世界上没有两片完全相同的叶子,因此我们认为,为Leaf的对象进行拷贝是没有意义的,因为你无法复制一个独一无二的东西,所以你想让以下代码无法编译通过:
Leaf L1;
Leaf L2;
Leaf L3(L1); //企图拷贝L1
L1 = L2; //企图拷贝L2;
通常如果你不希望 class支持某特定功能,只要不声明对应函数就是了。但这个策略对copy构造函数和copy操作符却不起作用,因为上面已经说明,如果你不声明它们,当尝试调用它们时,编译器会为你声明它们。
这把你逼到了一个困境。如果你不声明copy构造函数或 copy操作符,编译器可能为你产出一份,于是你的class支持copy,如果你声明它们,你的 class还是支持 copy,但你的目的却是要阻止 copy。
一个地方可以入手,因为所有编译器生成的函数都是 public.为阻止这些函数被创建出来你得自行声明它们,但这里并没有什么需求使你必须将它们声明为 public.因此你可以将copy构造函数或 copy操作符声明为 private.明确的声明一个成员函数来阻止编译器暗自创建其专属版本,并且令这些函数为 private,阻止调用它。
一般而言这个做法也并不绝对安全,因为 member函数和 friend函数还是可以调用你的 private函数。除非你够聪明,不去定义它们,那么如果某些人不慎调用任何一个,会获得一个连接错误( linkage error)。
实现方式:
class Leaf
{
public:
...
private:
Leaf(const Leaf& ); //参数名称并非必要,只不过大家习惯写出来
Leaf& operator=(const Leaf& ); //只声明
};
其他做法:
将连接期错误移至编译期是可能的(而且那是好事,毕竟越早侦测出错误越好),只要将copy构造函数和 copy操作符声明为 private就可以办到,但不是在 Leaf自身,而是在一个专门为了阻止 copying动作而设计的 base class,这个 base class 非常简单:
class Uncopyable
{
protected: //允许对象构造和析构
Uncopyable() { }
~Uncopyable() { }
private:
Uncopyable(const Uncopyable& ); //但阻止 copy
Uncopyable& operator=(const Uncopyable& );
};
为求阻止Leaf对象被拷贝,我们可以做的就是继承 Uncopyable:
class Leaf: private Uncopyable {...};
//class不再声明copy构造函数或copy操作符
总结:
编译器可以暗自为 class 创建 default构造函数、copy构造函数、copy操作符,以及析构函数。
为阻止编译器自动提供的功能,可将相应的成员函数声明为 private并且不予实现。或使用像 Uncopyable这样的 base class也是一种做法;