EC读书笔记系列之12:条款22、23、24

条款22 将成员变量声明为private

记住:

★切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性。

★protected并不比public更具封装性

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

记住:

★宁可拿non-member-non-friend函数替换member函数。这样可增加封装性、包裹弹性和机能扩充性。

-------------------------------------------------------------------------------

举例:

class WebBrowser {
    ...
    void clearCache();  //清除缓存
    void clearHistory();//清除历史
    void clearCookies();//清除cookies
    ...
};

假设想一次性执行上面三个有关清理的函数,有两种方法:

方法一:将这个一次性清理工作函数写成member function

class WebBrowser {
    public:
        ...
        void clearEverything(); //调用上面的三个函数
        ...
};

方法二:写成non-member-non-friend函数:

void clearBrowser( WebBrowser &wb ) {
    
    wb.clearCache();
    wb.clearHistory();
    wb.clearCookies();
}

方法二要好一点!!!因为non-member函数将导致较大的封装性,∵其并不能访问class内部的private成分。(因为对于对象内的数据来说,愈少代码可以看到数据,也即访问它,愈多的数据就可被封装,而我们也就愈能自由地改变对象的数据,这不也就是封装性的体现吗!!)

两件需要注意的事:

一、上述论述仅适用于no-member-non-friend函数;

二、因在意封装性而让函数“成为class的non-member”,并不意味着其“不可以是另一个class的member”

推荐做法

  让clearBrowser成为一个non-member并且位于WebBrowser所在的同一个namespace内:

namespace WebBrowserStuff {
    class WebBrowser {...};
    void clearBrowser( WebBrowser &wb );
    ...
}     //注意namespace这里无分号!!!

注:像上面WebBrowser这样的class可能拥有多种便利函数,如某些与书签有关,某些与打印有关等。可以将这些便利函数的编译相依关系降低:分离他们的直接做法是将书签相关的便利函数声明于一个头文件,将打印相关的便利函数声明于另一个头文件:

namespace WebBrowserStuff {

    class WebBrowser{...};  //核心机能
}

//头文件"webbrowserbookmark.h"
namespace WebBrowserStuff {

    ... //与书签相关的便利函数
}

//头文件"webbrowserprint.h"
namespace WebBrowserStuff {

    ... //与打印相关的便利函数
}

以此种方式切割机能并不适用于member函数,因为一个class必须整体定义,不能被分割为片段,从这个角度来说也符合本条款的主题。

条款24 若所有参数皆需类型转换,请为此采用non-member函数

记住:

★若需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么此函数必须是个non-member。

-------------------------------------------------------------------------------

举例:有理数类

class Rational {
    public:
        Rational( int numerator = 0,
                  int denominator = 1 
                ); //构造函数刻意不为explicit,
                //这样做是为了int-to-Rational隐式转换
        ...
};

现让该有理数类支持乘法运算。现将operator*写为类的member函数:

class Rational {
    public:
        ...
        const Rational operator*( const Rational &rhs ) const;
};

若下面这样用:

Rational oneEighth( 1, 8 );
Rational oneHalf( 1, 2 );
Rational result;
result = oneHalf*oneEighth;    //用法1:可以
result = oneHalf*2;             //用法2:可以
result = 2*oneHalf;             //用法3:错误

这里的用法2,之所以行得通,是因为这里发生了隐式类型转换,等价于下面这样调用:

  const Rational temp(2);  //建立一个暂时性的Rational对象

  result = oneHalf*temp;  //等价于oneHalf.operator*( temp );

当然这种做法可行的前提是non-explicit constructor!!!

仅当参数被列于参数列内,此参数才是隐式类型转换的合格参与者,这是用法2和用法3的区别。

解决方案:将operator*写成non-member函数,这样便可以使在每一个实参身上执行隐式类型转换!!!

const Rational operator*( const Rational &lhs,
                          const Rational &rhs
                        ) {

    return Rational( lhs.numerator() * rhs.numerator(),
                     rhs.denominator() * rhs.denominator()
                    );
}
这样:
result = oneHalf*2;            
result = 2*oneHalf;            //也可以通过编译了!!!

注意:

member函数的反面是non-member函数,而不是friend函数!!!(因为有太多的C++程序员错误地认为若一个“与某class相关”的函数不该成为一个member,就该是个friend)

原文地址:https://www.cnblogs.com/hansonwang99/p/4941551.html