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

2008-02-23 05:40:45来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

  假如您不想使用编译器为您产生的函数,就明确拒绝

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

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 的基类是方法之一。




标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: C 箴言:多态基类中将析构函数声明为虚拟

下一篇: C 箴言:了解C 偷偷加上和调用了什么

热门词条
热门标签