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


    1、问题引出

    (1)假设有一个WebBrower类,代表浏览器,其中有的成员方法有:
    • 清除缓存
    • 清除url
    • 清除cookies
    class WebBrower
    {
    public:
        void ClearCach();
        void ClearHistory();
        void RemoveCookies();
    };
    
    (2)现在有一个需求:把这三个同时同时清除。但是我们实现上述需求有两种方式:
    方式一:在WebBrower类中再写一个成员方法,直接在其内部调用其他成员方法。
    class WebBrower
    {void ClearEverything()
        {
            ClearCach();
            ClearHistory();
            RemoveCookies();
        }};
    
    
    方式二:写一个不属于这个类的函数,在函数内调用这个类的三个成员方法。
    void ClearWebBrowser(WebBrower& w)
    {
        w.ClearCach();
        w.ClearHistory();
        w.RemoveCookies();
    }
    

    现在的问题是,哪一种实现方式更好?也就是把它写成成员函数好,还是写成 non-member 、non-friend 函数好呢?

    答案是 写成:non-member、non-friend 好。

    2、为什么写成non-member、non-friend 好?

    首先,对于面向对象的一个误解:数据应该和操作数据的函数绑定在一起。如果按照这种解释,那么应该写成member的。但是实际上,面向对象强调的是封装性。

    • 面向对象真正强调的是封装性,对于上述问题,non-member、non-friend函数的封装性要比member函数好。
    • non-member函数允许对浏览器类有较大的包裹弹性,较大的包裹弹性,将导致较低的编译相依度,增加浏览器类的可延展性。

    3、关于上述两个原因的进一步解释一:封装性

    (1)为什么强调封装性?

    所谓封装就是不可见,越多东西被封装,能够看见它的人越少。越少的人看到它,我们就能够更大弹性的修改它。因此,封装性越好,我们改变实现的能力就越高。推崇封装的原因:我们能够改变事物,而只影响有限的客户。

    (2)如何衡量封装性?

    我们计算能够访问该数据的成员函数以及其它函数的数量,作为一种粗糙的衡量。越多的函数能够访问它,它的封装性就越低。
    例如:public数据,所有的函数都可以访问它,它就是毫无封装性的。private数据,只有friend和member函数可以访问它,它的封装性的高低,就和能够访问它的friend函数和member函数数量有关,数量越大,代表封装性越低,数量越小,代表封装性越高。

    总之,在实现同一机能的情况下,面对使用member函数和non-member、non-friend函数的抉择时,后者提供更好的封装性。

    这就从封装性的角度解释了上述例子。

    (3)注意

    关于上述论断,有两个需要注意的点。
    第一: non-member、friend函数 和member函数时一样的,都可以访问私有数据。因此只是non-member、non-friend 的函数和member函数之间存在封装性程度的高低不一。

    第二:可以将函数写成另一个类类的member函数。例如,可以使得ClearWebBrowser() 函数称为另外一个工具类的static member函数。non-member指的是不能将其写成浏览器类WebBrower 的成员函数。(这对于其它语言的程序员来说一个温暖的慰藉,因为有些语言只能将函数写到class内部。)

    4、关于上述两个原因的进一步解释二:编译相依度、包裹弹性

    虽然可以将其写到其它类中,但是C++比较自然的做法是,将它写成一个non-member函数,并让它和浏览器类在同一个命名空间中。

    这样的做法是由原因的:

    (1)原因一:可以降低文件间的编译相依度。

    namespace 和class 是不同的,namespace是可以跨越多个文件的,但是class却不能。class 内的是核心技能,但是便利函数只是提供便利的,可有可无的。即使没有便利函数,用户可以通过访问class进行相关的操作。因此说便利函数时外覆的。

    一个类可以由不同的机能分化出拥有多个便利函数,与cookies管理有关的、与书签有关的、与打印有关的。用户可能只对其中一部分感兴趣,那就没有必须让他们之间存在编译相依的关系。可以将他们进行分离,与不同模块相关的便利函数写到不同的头文件中,这样用户对哪个模块感兴趣,就包含哪个头文件就可以了。

    (2)原因二:可以轻松的扩展像这样的便利函数。(体现包裹弹性)

    将所有便利函数放在多个头文件中但隶属于同一个命名空间,意味着客户可以轻松扩展这一组便利函数。他们需要增加什么便利函数时,也添加到这个命名空间就好。class就不能这样扩展。

    注意: 原因一种组织代码的 方式也正是C++标准程序库的组织方式。C++并没有将所有的功能都写到一个头文件里,而是写成数十个头文件,每个头文件中包含某些机能。用户需要什么,就包含什么。这样形成一个编译相依的小系统。 这种分割机能的方式不适用于class成员函数,因为class必须整体定义,不能被分割成片段。

  • 相关阅读:
    Python Django 编写一个简易的后台管理工具2-创建项目
    leetcode-解题记录 771. 宝石与石头
    leetcode-解题记录 1108. IP 地址无效化
    Python Django 编写一个简易的后台管理工具1-安装环境
    备忘录
    Pollard_rho 因数分解
    ProgrammingContestChallengeBook
    HDU ACM-Steps
    ARCH-LINUX 折(安)腾(装)记
    各种Python小玩意收集
  • 原文地址:https://www.cnblogs.com/lasnitch/p/12764172.html
Copyright © 2020-2023  润新知