Effective C++学习第七天

条款23:宁以non-memeber、non-friend替换member函数

          non-member/non-friend可以给对象带来更大的封装性,从两个方面来考虑:1)考虑封装,越多东西被封装,它们就越不可见,就越少人看到它,就会越有弹性去改变它;2)考虑对象内的数据,越少代码可以看到数据,越多的数据被封装,那么我们就越能自由地改变对象数据;

          在C++中,可以让所有的non-member函数放在同一个命名空间内,然后使用那个命名空间;C++标准程序库并不是单一、整体、庞大的头文件,而是很多头文件组合而成的,这就允许客户只对他们所有的那部分系统形成编译相依;

条款24:若所有参数都需类型转换,用non-member代替member函数

             对于参数都允许发生隐式转换的函数,使用non-member函数可以使你的类功能更加具有一致性,而且还支持混合式算术编程;具体代码分析如下:

         const rational operator*(const rational&rhs)const{

               return rational(this->numerator( )*rhs.numerator( ),this->denominator( )*rhs.denominator( );

}

rational onehalf(1,2);

rational result=2*onehalf;//错误,调用实际情况是2.operator(&2,onehalf);//不能对常量取地址操作

rational result=onehalf*2;//正确

修改为non-member函数

const rational operator*(const rational& lhs ,  const rationa & rhs){

      return rational(lhs.numerator( )*rhs.numerator( ),lhs.denominator( )*rhs.denominator( );

}

结论:如果你需要为某个函数的所有参数(this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member;

条款25:考虑写出一个不抛异常的swap函数

             swap原本是STL中的一部分,后成为异常安全性编程的脊柱以及用来处理自我赋值可能的一种常见机制;

 1.缺省情况下,std标准库的swap算法:

                  template<typename T>

                  void swap(T&a,T&b){T temp(a);a=b;b=temp;}//实现是基于T的copy构造函数和copy assignment完成

2.通常情况下,数据的表现形式都是“以指针指向一个对象,内含真正的数据”,这种设计模式通常变现为pimpl(pointer to implementation)手法,如:

class widgetimpl{                                            class widget{

private:                                                             private:

          int a ,b,c;                                                           widgetimpl*pimpl;

          std::vector<double>v;                              };

}

如果需要交换两个widget对象,那么我们唯一需要做的就是交换两个pimpl指针,但是普通的swap算法却是复制了三次widgetimpl;

解决上述问题的一个方法:就是将std::swap针对widget全特化(模板函数的一个实例),然后用widget的成员函数调用它(写错了)用全特化的swap函数来调用public swap成员函数,具体代码如下:

class widget{                                                       namespace std{

public:                                                                            template<>//针对widget的特化版本

          void swap(widget&other){                           void swap<widget>(widget&a.widget&b){

          using std::swap;//让std内的swap可用                            a.swap(b);            //调用swap成员函数;

         swap(pimpl,other.pimpl);                                  }

};                                                                       }

3.如果widget和widgetpimpl都是class templates而非class,那么我们重新定义非成员函数;

       template<typename T>

      void swap<widget<T>>(widget<T>&a,widget<T>&b){ a.swap(b);}       //不合法,错误

原因是我们企图对function template偏特化,但是C++只允许对class template偏特化;

引申:std是一个特殊的命名空间,其管理规则也比较特殊,客户可以全特化(实例化)std内的template,但是不可以添加新的templates(class或者templates或者其他的任何东西)到std里头,C++禁止这类行为,但是编译器却不会报错,但是软件可能会出现不可预期的行为;

    解决上述问题的一个办法:添加一个重载函数来代替我们要做的偏特化一个function template行为,我们还是声明一个non-member swap函数让他调用member swap,但不再声明将那个non-member swap声明为std::swap的特化版本,具体实现代码如下:

 template<typename T>                                                   template<typename T>

class widget{                                                                     void swap(T&obj1,T&obj2){

public:                                                                                         using std::swap;

        void swap(widget&other){                                                   swap(obj1,obj2);//T型对象的最佳调用swap版本

          using std::swap;                                                     }   //优先调用T专属版本,即public swap成员函数                    

         swap(pimpl,other.pimpl);                                         //并在该版本不存在的情况下调用std内一般化的版本

}

结论:1)如果swap缺省实现码可以实现你的class或者class template提供可接受的效率,你不需要做任何事情;

          2)如果swap缺省码不够实现你的效率,你可以:

               a)提供一个public swap成员函数,让它处理两个对象值;

               b)在你所在的class或者template所在的命名空间内提供一个non-member swap,并令它调用上述swap成员函数;

               c)如果你在编写一个class(而非class template),为你的class特化std::swap,并令它调用你的swap成员函数。

        3)如果你调用swap,确定包含一个using声明式,以便std::swap在你的函数内曝光,然后不加namespace,单纯调用swap(使swap函数有更多的选择);

成员版的swap绝不可能抛出异常,因为swap的一个最好应用就是帮助classes提供强烈的异常安全性保障(以copy构造函数和copy assignment操作符为基础的);

引申:关于模板的全特化和偏特化描述,可以参考博文:https://www.cnblogs.com/yyehl/p/7253254.html


猜你喜欢

转载自blog.csdn.net/xx18030637774/article/details/80804724