[Effective C++]条款23:宁以non-member、non-friend替换member函数

203 阅读2分钟

本条款强调封装性优先于类的内聚逻辑

看书上的例子:

class WebBrower 
{ 
    public: void ClearCach(); 
    void ClearHistory(); 
    void RemoveCookies(); 
};

定义了一个WebBrowser类, 在类里面执行清理缓存, 清历史记录和清理cookies. 如果将这三个小函数写进一个函数里面, 那么这个大函数放在类内还是类外呢?

(cookie是操作系统三级存储系统中的一级,三级存储系统包括cookie,内存,硬盘。缓存只是在内存中开辟的空间,不是一个概念)

  • 放在类内:
class WebBrower 
{ 
    ....
    void ClearEverything()
    {
        public: void ClearCache(); 
        void ClearHistory(); 
        void RemoveCookies(); 
    }
    ....
};

  • 放在类外:
void ClearWebBrowser(WebBrowser& web)
{
    web.ClearCache();
    web.ClearHistory();
    web.RemoveCookies();
}

根据面向对象的基本概念, 应该把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象. 这就意味着我们应该把它放在类内里面, 然而, 如果从封装性上看, 这个大函数应该放在类外。

  • 如果位于类内: 意味着这个函数除了访问到这三个小的清理函数之外, 还可以访问到类内的私有成员. 随着产品功能的扩充, 这个函数很可能逐渐丰富起来, 越丰富就说明封装行越差, 一旦功能发生变更, 改动的地方会很大.

  • 如果放在类外, 通过传入WebBrowser的对象来对类的public函数进行访问的, 这个函数是不可能访问private成员变量的. 因此放在类外的封装性会强于放在类内. 要注意一点, 类外的ClearWebBrowser 不能是WebBrower的友元函数, 因为友元函数可以直接访问类的privateprotect成员变量.

把这个清除的函数放在类外, 并不能破坏和类内的关联, 书上提供了namespace解决方案, eg:

namespace WebBrowserStuff
{
    …
    class WebBrowser();
    void ClearWebBrowser(WebBrowser& w);
    …
}

namespace和class不同, namespace可以跨越多个源码文件后者并不能. namespace是开放的,和class不同的是你可以在多个文件里面象同一个namespace里面添加东西。比如stl里面的东西都是在名字空间std里面,但是却定义在了多个文件里面。命名空间的捆绑, 是在封装和内聚之间很好的平衡.

最后我们总结一下这个遵循这个条款的好处

  • 1.增加代码的封装性
  • 2.提高代码的扩展性
  • 3.降低代码的编译依存性。