Effective C++学习笔记之第四章(5)

来源:互联网 发布:爱淘宝0.5红包入口 编辑:程序博客网 时间:2024/05/16 10:04

chapter 4 设计与声明

item24:当所有参数都需要隐式类型转换的时候,使用non-member函数
1)如果一个函数的每一个参数都需要一个类型转换(包括可能的this指针),那么这个函数一定是一个non-member函数。下面一步步来分析,假设有一个实数类,需要进行乘法运算:

//实数类的声明class Rational {public:  // ctor is deliberately not explicit;allows implicit int-to-Rational conversions  Rational(int numerator = 0,int denominator = 1);    int numerator() const;             // accessors for numerator and  int denominator() const;           // denominator — see Item 22private:  ...public:  //假设实数类里面有一个operator*的函数,先不考虑上一个item讲得关于封装性的考虑  const Rational operator*(const Rational& rhs) const;};//如果这样调用,OKRational oneEighth(1, 8);Rational oneHalf(1, 2);Rational result = oneHalf * oneEighth; // fineresult = result * oneEighth;           // fine//但是,如果需要进行混合运算result = oneHalf * 2;         // fine    代码1result = 2 * oneHalf;        // error!  代码2//上述代码的等价形式result = oneHalf.operator*(2);   // fine result = 2.operator*(oneHalf);  // error!//代码1的进一步的等价形式const Rational temp(2);   // create a temporary Rational object from 2result = oneHalf * temp;  // same as oneHalf.operator*(temp);

按理来说,乘法运算是可以交换的,没理由说支持某一种顺序,而另一种顺序就是错误的。当然如果Rational的构造函数是explicit的,那么代码1也是错误的,达到一致性了,呵呵。现在分析代码2,由于2是一个整型,所以没有对应的operator*函数,所以编译器会在non-member函数里面去寻找类似于result = operator*(2, oneHalf);但是这个函数不存在,所以最后会出现编译错误。为什么不能上面两个代码,一个可以,而另一个不可以呢。先说代码1,2作为operator*的参数,传进去的时候进行了一个隐式转换,而第二种方法,2是this指针所指的,不是通过参数传的,所以没办法转化。所以这里需要一个non-member函数。

const Rational operator*(const Rational& lhs,const Rational& rhs){  return Rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());}Rational oneFourth(1, 4);Rational result;result = oneFourth * 2;  // fineresult = 2 * oneFourth;  // hooray, it works!

2)那这里需不需要把operator*作为friend函数呢?答案是no,因为operator*完全可以由Rational的公共接口实现。当然这也揭示了一个道理:与member function对应的是non-member function而不是friend function。不是说一个函数不是member function就一定是friend function。现实生活中,可能朋友也会带来麻烦呢,是吧~
3)当涉及到template C++而不是面向对象C++时,可能会有新的情况需要考虑(见item 46)。so,keep moving.

item25:考虑写出一个不抛出异常的swap函数
1)如果swap的缺省实现码对你的class或者class template提供可接受的效率,那你不需要额外任何事。
2)如果缺省的swap实现版的效率不足(比如你的class或者template中包含了指向实现的指针(pimpl idiom),那么就可以尝试着做一下几件事来提高swap效率。
3)提供一个public swap成员函数,高效的置换类型的两个对象值,比如说涉及到指针的时候,只置换指针的地址即可。这个函数绝不该抛出异常。因为swap缺省版本是以copy构造函数和copy赋值操作符为基础的,而一般情况下两者都允许抛出异常。而你特化的swap一般来说跟默认的swap具有相同的特性。因为你写的swap基本上都是对内置类型进行操作,而内置类型上的操作绝不会抛出异常。
4)在你的class或者template所在的命名空间提供一个non-member的swap,如同item24里面所讲的一样,然后用它来调用上述的swap成员函数。当然如果你的class的定义和这个non-member的swap如果不是另外放在一个命名空间中(也就是和别的class或者typedef什么的混在一起),其功能也可以实现,但是这样一来,不觉得代码很乱?如果是class(非template),同时为class特化std::swap。(PS:说实话,感觉这句话我没理解,可能是因为我对C++的泛型编程不了解,当然希望有人路过的时候留言讨论,如果有新的感悟,我再加上来)

//4)的代码说明namespace WidgetStuff {  ...                                     // templatized WidgetImpl, etc.  template<typename T>                    // as before, including the swap  class Widget { ... };                   // member function  ...  template<typename T>                    // non-member swap function;  void swap(Widget<T>& a,Widget<T>& b)   // not part of the std namespace{    a.swap(b);  }}//5)的代码说明template<typename T>void doSomething(T& obj1, T& obj2){  using std::swap;           // make std::swap available in this function  ...  //正确的调用方式,call the best swap for objects of type T  swap(obj1, obj2);           //错误的调用方式,这样编译器就会默认的只在std命名空间去查找合适的swap函数  std::swap(obj1, obj2);  ...}

5)如果调用swap,记得包含一个using声明,使得std::swap可用,并且不要加任何修饰符,直接调用swap。
6)为"用户定义类型"进行std template全特化是好的,但是不要尝试在std内加入某些对std而言全新的东西。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 喝玫瑰花茶胃疼怎么办 卡地亚戒指掉色怎么办 苹果8plus掉电快怎么办 苹果8plus耗电快怎么办 卡地亚戒指划痕怎么办 苹果手机玫瑰金掉色怎么办 14k玫瑰金掉色怎么办 卡地亚手镯掉色怎么办 手机掉油漆里了怎么办 黄金戴久了变黑怎么办 玫瑰金褪色不亮了怎么办 黄金带久了不亮怎么办 玉石带久了不亮怎么办 手表带久了不亮怎么办 蜜蜡带久了不亮怎么办 钛钢首饰不亮了怎么办 潘多拉玫瑰金戒指褪色了怎么办 金色手表漆掉了怎么办 玫瑰金表带褪色后怎么办 K金褪色或泛黄怎么办 钛钢玫瑰金变黑怎么办 玫瑰金手镯掉色了怎么办 彩金颜色不亮了怎么办 玫瑰金链子黑了怎么办 18k玫瑰金变黑了怎么办 玫瑰金戒指遇到火变黑怎么办 18k白金发黄了怎么办 18k金掉色后怎么办吗 dw手表金色掉漆怎么办 dw玫瑰金手表褪色怎么办 dw金色表带黑了怎么办 机械表机芯坏了怎么办 银镀玫瑰金褪色怎么办 苹果七p玫瑰金掉漆怎么办 美度镀金表掉色怎么办 吃了发黑的香菇怎么办 脸上的皮肤暗黄怎么办 吃了变黑的香菇怎么办 怀孕喝了玫瑰茶怎么办 孕妇爱喝茉莉茶怎么办 干菊花生虫了该怎么办