设为首页 加入收藏

TOP

关于接口的设计与声明--对封装性的理解[C++](三)
2016-04-29 12:55:03 】 浏览:1101
Tags:关于 接口 设计 声明 封装 理解
t Rational& rhs) { return Rational(lhs.n1*rhs.n1, rhs.n2*rhs.n2); }

5. 将成员变量声明为private

在我们最初学习C++ OOP时就有一天准则,成员变量总是要声明为private。本节我们来讨论为何成员变量要被声明为private。

理由一:语法一致性。
因为成员变量不是public,客户唯一能够访问对象的办法就是通过成员函数。如果public接口内的每一样东西都是函数,客户就不用纠结调用他时是否需要使用小括号。如此便能省下大量的时间。 理由二:使用函数可以让你对成员变量的处理有更准确的控制。
如果成员变量是public,那么每个人都可以对他进行读写,但如果你以函数取得或设定其值,就可以实现“不准访问”,“只读访问”,“读写访问”等访问控制。
如以下代码:
class AccessLevel {
private:
    int noAccess;
    int ReadOnly;
    int WriteOnly;
    int readWrite;
public:
    // ...
    int getReadOnly() {
        return ReadOnly;
    }
    void setWriteOnly(int i) {
        WriteOnly = i;
    }
    void setreadWrite(int i) {
        readWrite = i;
    }
    int readreadWrite() {
        return readWrite;
    }
};

如此精细地对各个数据成员进行访问限制是有必要的。

理由三:封装!
这是最有说服力的理由了!C++ OOP其中最重要的一条性质就是封装性!将数据成员封装在接口的后面,可以为“所有可能的实现”提供弹性。
封装的重要性比我们最初见到它时更重要。如果我们对客户隐藏成员变量,就可以确保class的约束条件受到维护,因为只有成员函数可以影响他们。public意味着不封装,而几乎可以说不封装意味着不可改变,特别是对被广泛使用的class而言。被广泛使用的class是最需要封装的一个族群,因为他们能够从“改采用一个教佳实现版本”中获益。

我们继续来讨论protected的封装性。

一般人会认为protected比public更具有封装性。其实不然。更准确的判断方法是:某些东西的封装性与“当其内容改变时可能造成的代码破坏量”成反比。所谓改变,也许是从class中移除他。于是乎,我们可以进行以下分析。对于public的成员变量,如果我们移除他,意味着我们要破坏所有使用它的客户代码。(破坏量很大吧)而对于protected的成员变量呢,如果我们移除它,意味着要破坏所有derived class(破坏量也很大吧?)因此protected和public的封装性其实是一样的。这也就意味着,一旦我们决定把某个成员变量声明为public或protected,就很难改变某个成员变量所涉及的一切。

结论就是,其实只有两种访问权限:private(实现封装)和其他(不实现封装)

6. 宁以non-member、non-friend替换member函数

面向对象守则要求,数据以及操作数据的那些函数应该被捆绑在一起,这意味着它建议所有操作数据成员的函数都应该是member函数。然而事实上是如此吗?

问题产生

假设我们希望写一个类来描述网页:

class WebBrowser {
public:
    ...
    void clearCache();
    void clearHistory();
    void removeCookies();
    ...
    // 用户希望有一个函数能够清楚所有信息
    // 问题是,该函数是否应该声明为member?
    void clearEverything();
};
// 也可以声明为non-member
void clearEverything(WebBrowser &web) {
...
}

那么哪种选择更好呢?

问题解决

根据面向对象守则要求,声明为member函数应该是更好的选择。然而,这是对面向对象真实意义的一个误解。面向对象要求数据应该尽可能被封装,然而与直观相反地,member函数clearEverything带来的封装性比non-member函数的低。此外,提供non-member函数可允许对WebBrowser相关机能有更大的包裹弹性,从而最终导致较低的编译相依度,增加WebBrowser的可衍生性。以下我们给出理由。

封装性。愈多的东西被封装,越少人可以按到它,那么我们就有越大的弹性去改变它,而我们的改变只会影响看到改变的那些人和事物。这就是我们推崇封装性的原因:它使我们能够改变事物而只影响有限客户。 考虑对象内数据。越少代码可以看到数据,越多的数据可被封装,而我们也就越能自动地改变对象数据。越多的函数可以访问数据成员,数据的封装性就越差!

因此,因为non-member non-friend函数不能直接改变数据成员,因此他就可以最大限度的实现封装

解答优化

在C++中,最自然的做法,是让clearEverything称为一个non-member函数并且位于WebBrowser所在的同一个namespace内:

namespace WebBrowserStuff {
    class WebBrowser {...};
    void clearEverything(WebBroswer &web);
    ...
}

namespace和class是不用的!前者可以跨越多个源码文件而后者不能,这很重要!

像clearEverything这样的函数就是便利函数,虽然没有对WebBrowser有特殊的访问权限,但可以极大的便利客户。而实际上,我们会补充大量的类似的便利函数,并且他们可能分属于不同的模块,于是我们便采用把不同模块便利函数写于不同的头文件中,但他们都隶属于同一个命名空间:

#include "webbrowser.h" 提供class声明本身,以及其中核心机能
namespace WebBrowserStuff {
class WebBroser { ... };
    ...  // 核心机能,几乎所有用户都需要的non-member便利函数
}

// 头文件 “webbrowserbookmarks.h" 
// 与标签相关
namespace WebBrowserStuff {
    ... // 与标签相关的便利函数
}
// 头文件 ”webbrowsercookies.h"
namespace WebBrowserStuff{
    ... // 与cookie相关的便利函数
}
...

注意这是C++标准程序库的组织方式。标准程序库中并不是拥有单一、整体、庞大的

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

令class支持隐式类型转换通常是个糟糕的注意。当然也有例外,例如你在建立数值类型时。

问题产生

假设我们需要设计一个有理数类:

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);
    int numerator() const;
    int denomin
首页 上一页 1 2 3 4 5 下一页 尾页 3/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇nyoj 712 探 寻 宝 藏(双线dp 第.. 下一篇nyoj 711最舒适的路线(第六届河南..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目