zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    Effective C++ 33 避免遮掩继承而来的名称
    求子数组之和的最大值——编程之美 2.14 扩展问题 正确实现
    数据结构快速回顾——平衡二叉树 AVL (转)
    位运算符 优先级 折半搜索
    关于 二维数组指针
    C++中的单例模式(转)
    C# ThreadStart和ParameterizedThreadStart区别
    C# 实现数字字符串左补齐0的两种方法
    C# 窗口自适应窗口宽度和高度
    C# 在多线程环境中,进行安全遍历操作
  • 原文地址:https://www.cnblogs.com/hansonwang99/p/4941551.html
Copyright © 2011-2022 走看看