vlambda博客
学习文章列表

如果不想使用编译器默认生成的函数,请明确拒绝它!

点蓝色字关注“CurryCoder的程序人生”


1.问题引入

试想如下情形,某个房地产商所拥有的房子都是不同的。同时,你在为这个公司设计程序,而你不想将一座房子的信息拷贝给另一座房子,这就需要禁止使用拷贝构造函数和拷贝赋值运算符(copy assignment operator)。如下例所示:
 1#include <iostream>
2
3class House
4{

5    // ...
6};
7
8int main()
9
{
10    House h1;
11    House h2;
12    House h3(h1);  // 禁止执行默认的拷贝构造函数
13    h1 = h2;  // 禁止执行默认的拷贝赋值运算符
14
15    return 0;
16}
如果是其他功能,不想使用的话不声明即可。但是对于以上这两个功能,像昨天的文章所说的。即使你不声明不定义,编译器还是会为该类自动生成的。

2.解决方法

解决方法:首先,因为编译器为类自动生成的函数都是共有(public)的,所以我们就可以把这些函数声明为私有(private),这样类生成的对象就无权限调用它们。其次,只声明不定义。因为如果有定义,本类和友元函数(friend functions)还是可以调用它们。如下例所示:

 1#include <iostream>
2
3class House
4{

5public:
6    // ...
7private:
8    House (const House&);  // 只声明,不定义拷贝构造函数。同时要设为private!由于根本就不会用到它们,所以可以不必写形参了
9    House& operator=(const House&); // 只声明,不定义拷贝赋值运算符。同时要设为private!由于根本就不会用到它们,所以可以不必写形参了
10};

因此,当你在代码中试图拷贝对象,编译器就会报错。然而,如果你不小心在该类的成员函数或者友元函数中调用了private权限自动生成的函数,由于找不到自动生成函数的定义,链接器则会出错。但是,将所有报错提前到编译器不是更好吗?这样就可以越早的检测出错误了。


3.更好的解决方法

昨天的文章中也提到了,当一个父类将拷贝构造函数和拷贝赋值运算符声明为私有时,编译器会拒绝为它的子类生成拷贝构造函数和拷贝赋值运算符因此我们可以专门使用一个父类,在父类当中声明拷贝操作为私有(private),并让我们的类继承自它。这样,当成员函数或友元函数试图拷贝House对象时,所有关于拷贝的操作都会提前在编译阶段报错。如下例所示:

 1#include <iostream>
2
3class Uncopyable{
4protected:
5    Uncopyable(){}
6    ~Uncopyable(){}
7private:
8    Uncopyable(const Uncopyable&);
9    Uncopyable& operator=(const Uncopyable&);
10};
11
12class House: public Uncopyable{
13// ...
14};

4.总结
(1).当不想让编译器为类自动生成某些函数时,把这些不想要的函数声明在此类的私有成员中并不予以定义。或者,更好的方法使用像上例中定义一个Uncopyable父类,并让我们的House类公有继承自它。

觉得好看,请点这里↓