快捷搜索:

C++箴言:拒绝不想用的编译器产生的函数

假如你不想应用编译器为你孕育发生的函数,就明确回绝

不动产代理商出售房屋,办事于这样的代理商的软件系统自然要有一个类来表示被出售的房屋:

class HomeForSale { …… };

每一个不动产代理商都邑很快指出,每一件家当都是独特的——没有两件是完全一样的。在这种环境下,为 HomeForSale 工具做一个拷贝的设法主见就令人不解了。你怎么能拷贝一个环球无双的器械呢?最好让这种类似妄图拷贝 HomeForSale 工具的行径不能经由过程编译:

HomeForSale h1;

HomeForSale h2;

HomeForSale h3(h1); // attempt to copy h1 - should

// not compile!

h1 = h2; // attempt to copy h2 - should

// not compile!

唉,防止这种编译的措施并非那么简单易懂。平日,假如你不盼望一个 class 支持某种功能,你可以简单地不声明付与它这种功能的函数。这个策略对付拷贝赋值运算符不起感化,由于,就象 Item 5 中指出的,假如你不声明它们,而有人又想调用它们,编译器就会隐式地声明它们。

这就限定了你。假如你不声明拷贝构造函数和拷贝赋值运算符,编译器也可以为你天生它们。你的类照样会支持拷贝。另一方面,假如你声清楚明了这些函数,你的类依然会支持拷贝。我们在这里的目标便是防止拷贝。 办理这个问题的关键是所有的编译器天生的函数都是 public.为了防止天生这些函数,你必须自己声明它们,然则你没有来由把它们声明为 public.相反,应该将拷贝构造函数和拷贝赋值运算符声明为 private.经由过程显式声明一个成员函数,可以防止编译器天生它自己的版本,而且将这个函数声明为 private,可以防止别人调用它。

平日,这个规划并不十分保险,由于成员函数和友元函数照样能够调用 private 函数。换句话说,除非你不定义它们。那么,当有人不小心地调用了它们,在连接的时刻会呈现差错。这个秘诀——定义一个 private 成员函数却有意不去实现它——确凿不错,在 C++ 的 iostreams 库里,就有几个类用此措施防止拷贝。比如,看一下你用的标准库的实现中,ios_base,basic_ios 和 sentry 的定义,你就会看到拷贝构造函数和拷贝赋值运算符被声明为 private 而且没有定义的环境。

将这个秘诀用到 HomeForSale 上,很简单:

class HomeForSale {

public:

..

private:

...

HomeForSale(const HomeForSale&); // declarations only

HomeForSale& operator=(const HomeForSale&);

};

你会留意到,我省略了函数参数的名字。这没有需要,只是一个通俗的常规。终究,函数不会被定义,极少有时机被用到,有什么需要指定参数的名字呢?

对付上面的类定义,编译器将阻拦客户拷贝 HomeForSale 工具的妄图,假如你不小心在成员函数或者友元函数中这样做了,连接法度榜样会提出抗议。

将连接时差错提前到编译光阴也是可行的(早发明差错终究比晚发明好),不要让 HomeForSale 自己去声明 private 的拷贝构造函数和拷贝赋值运算符,在一个特意设计的基类中声明。这个基类本身异常简单:

class Uncopyable {

protected: // allow construction

Uncopyable() {} // and destruction of

~Uncopyable() {} // derived objects...

private:

Uncopyable(const Uncopyable&); // ...but prevent copying

Uncopyable& operator=(const Uncopyable&);

};

为了禁止拷贝 HomeForSale 工具,我们必须让它从 Uncopyable 承袭:

class HomeForSale: private Uncopyable { // class no longer

... // declares copy ctor or

}; // copy assign. operator

在这里,假如有人——以致是成员函数或友元函数——试图拷贝一个 HomeForSale 工具,编译器将试图天生一个拷贝构造函数和一个拷贝赋值运算符。就象 Item 12 解释的,这些函数的编译器天生版会试图调用基类的对应函数,而这些调用将被回绝,由于在基类中,拷贝操作是 private 的。

Uncopyable 的实现和应用包孕一些奥妙之处,比如,从 Uncopyable 承袭不能是 public 的(拜见 Item 32 和 39),而且 Uncopyable 的构造函数不必是 virtual 的(拜见 Item 7)。由于 Uncopyable 不包孕数据,以是它相符 Item 39 描述的空基类优化前提,但由于它是基类,此项技巧的利用不能引入多重承袭(拜见 Item 40)。反过来说,多重承袭无意偶尔会使空基类优化掉效(照样拜见 Item 39)。平日,你可以轻忽这些奥妙之处,而且此处只是用 Uncopyable 来做演示,由于它对照得当做广告。在 Boost(拜见 Item 55)中你可以找到一个可用的版本。那个类名为 noncopyable.那是一个好的 class,我只是发明那个名字有一点儿不(un-)……嗯……非自然(nonnatural)。

Things to Remember

·为了回绝编译器自动供给的功能,将响应的函数声明为 private,而且不要给出实现。应用一个类似 Uncopyable 的基类是措施之一。

您可能还会对下面的文章感兴趣: