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

2016-02-19 18:36 5 1 收藏

今天图老师小编给大家精心推荐个C++箴言:拒绝不想用的编译器产生的函数教程,一起来看看过程究竟如何进行吧!喜欢还请点个赞哦~

【 tulaoshi.com - 编程语言 】

假如你不想使用编译器为你产生的函数,就明确拒绝SCRIPT language=javascript>document.title="翻译:Effective C++, 3rd Edition, Item 6: 假如你不想使用编译器为你产生的函数,就明确拒绝 - "+document.title/SCRIPT
  
  不动产代理商出售房屋,服务于这样的代理商的软件系统自然要有一个类来表示被出售的房屋:
  
  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 的基类是方法之一。
  
  

来源:https://www.tulaoshi.com/n/20160219/1618303.html

延伸阅读
这是一个所有程序员都应该了解的小型的 C++ 词汇表。下面的条目都足够重要,值得我们对它们的含义务必取得完全一致。 声明(declaration)告诉编译器关于某物的名字和类型,但它省略了某些细节。以下这些都是声明: extern int x; // object declaration std::size_t numDigits(int number); // function declaration class Widget; /...
关于 const 的一件美妙的事情是它允许你指定一种语义上的约束:一个特定的对象不应该被修改。而编译器将执行这一约束。它允许你通知编译器和其他程序员,某个值应该保持不变。如果确实如此,你就应该明确地表示出来,因为这样一来,你就可以谋取编译器的帮助,确定这个值不会被改变。 关键字 const 非常多才多艺。在类的外部,你可以将它用...
函数参数的求值顺序 当一个函数带有多个参数时,C++语言没有规定在函数调用时实参的求值顺序。而编译器根据对代码进行优化的需要自行规定对实参的求值顺序。 !-- frame contents -- !-- /frame contents -- 有的编译器规定自左至右,有的编译器规定自右至左,这种对求值顺序的不同规定,对一般参数来讲没有影响。但是,假...
函数存放在内存的代码区域内,它们同样有地址,我们如何能获得函数的地址呢? 假如我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。 !-- frame contents -- !-- /frame contents -- 定义一个指向函数的指针用如下的形式,以上面的test()为例: ...
       SYNOPSIS[提要] Javac [options] [sourcefiles] [@files] oldjavac [options] [soUCefiles] [@files] 参数可疑位任意顺序 options 命令行选项 sourcefiles 一个或多个原文件 @files 一个或多个列有原文件列表的文件 DESCRIPTION[说明] javac工具读由java编程语言编写的类...

经验教程

251

收藏

17
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部