成员变量应该是private的
为什么不采用public成员变量?
某种东西的封装性与“当其内容改变时可能造成的代码破坏量”呈反比
protected变量的封装性是不是要比public的要好呢?答案是不一定。当public成员变量被修改,所有使用了它的客户代码破坏;当protected成员变量被修改,所有使用了它的derived class代码被破坏,而后进一步延伸
成员变量只有两种权限:private(封装)和非private(不封装)
总结:
//一个class用来表示网页浏览器 有的函数清理缓存 有的清理记录 有的清理cookie class WebBrowser{ public: ... void clearCache(); void clearHistory(); void removeCookie(); }; //很多用户想要一整个执行上述操作 两种方法member、non-member class WebBrowser{ public: ... void clearCache(); void clearHistory(); void removeCookie(); void clearEverything(); }; void clearEverything(WebBrowser &wb){ wb.clearCache(); wb.clearHistory(); wb.removeCookie(); } //哪种方法好呢?
non-member、non-friend函数更好,member函数带来的封装性要比non-member、non-friend函数低:
封装性:愈少的人看到他我们就有愈大的弹性去变化它,这是我们推崇封装性的原因,它使我们能够改变事物而只影响有限客户。考虑对象内的数据,越多的函数能够访问到它,说明它的封装性越差。增加non-member、non-friend函数不会降低对象内数据的封装性!!
编译相依
C++的做法是让class WebBrowser和non-member函数位于同一个namespace中
namespace WebBrowserStuff{ class WebBrowser{ public: ... void clearCache(); void clearHistory(); void removeCookie(); }; void clearEverything(WebBrowser &wb){ wb.clearCache(); wb.clearHistory(); wb.removeCookie(); } }
namespace和class不同,namespace可以跨文件而class不能
我们的namespace std不只有一个头文件<C++ standard library>,而是一共有几十个头文件,因为我们想用到vector就不需要queue等库,这样降低了编译相依
一个WebBrowser这样的class可能有大量的便利函数clearEverything,有些与书签相关、有些与cookie相关等,一个只与书签相关的便利函数不需要与cookie相关的便利函数发生编译相依。分离他们的直接做法就是放在不同的头文件中
//头文件webbrowser.h namespace WebBrowser{ class WebBrowser{ public: ... void clearCache(); void clearHistory(); void removeCookie(); }; } //头文件webbrowserbookmarks.h 书签相关的便利函数 namespace WebBrowser{ } //头文件webbrowsercookies.h cookies相关的便利函数 namespace WebBrowser{ } //把不同的non-member便利函数放在不同的头文件中可以降低编译相依
机能扩充性
这种方法切割不能用于class中的member函数,因为class是一个整体,不能被切割放在不同的头文件
将所有便利函数放在同一个命名空间中,意味着客户可以轻松扩展这一组便利函数,他们要做的就是在同一命名空间下建一个不同的头文件。比如WebBrowser中放一个下载相关的便利函数,只需要在WebBrowser的命名空间中新建一个下载相关的头文件。这样做对于class是不行的,因为class对于客户是不能修改的!!
总结:
当我们想要设计一个class表示有理数, 显然ints转化为这个有理数的隐性转换是合理的,所以构造函数是non-explicit的
//一个表现有理数的class class Rational{ private: int n, d;//n分子 d分母 public: Rational(int numerator = 0, int denominator = 1) : n(numerator), d(denominator){} int n() const; int d() const; };
现在我们想要实现operator*的方法,应该是member函数还是non-member呢?首先我们假定为member函数
//一个表现有理数的class class Rational{ private: int n, d;//n分子 d分母 public: Rational(int numerator = 0, int denominator = 1) : n(numerator), d(denominator){} int n() const; int d() const; const Rational operator*(const Rational &rhs) const; }; Rational oneEight(1, 8); Rational oneHalf(1, 2); Rational result = oneEight * oneHalf; result = result * oneEight;//正确 result = oneHalf * 2;//正确 result = 2 * oneHalf;//错误
上述语句等同于
result = oneHalf.operator*(2);//正确 //实际执行 //const Rational temp(2); //隐式转换 //result = oneHalf * temp; result = 2.operator*(oneHalf);//错误
结论:只有当参数被列于参数列中,这个参数才是隐式转换类型的合格参与者 ,被调用的成员函数所隶属的那个对象(this对象)绝不是隐式类型转换的合格参与者
所以我们应该用non-member函数来写operator*
//一个表现有理数的class class Rational{ private: int n, d;//n分子 d分母 public: Rational(int numerator = 0, int denominator = 1) : n(numerator), d(denominator){} int n() const; int d() const; }; const Rational operator*(const Rational&lhs, const Rational &rhs); Rational oneEight(1, 8); Rational oneHalf(1, 2); Rational result = oneEight * oneHalf; result = result * oneEight;//正确 result = oneHalf * 2;//正确 result = 2 * oneHalf;//正确
那么这个operator*需要是friend函数吗?本例不需要,因为它用的是Rational的public接口
所以一定记住member对立面是non-member,而不是freind,不要想着某个与类相关的函数不是member的,就应该是freind的!!!无论何时如果你能避免friend函数就应该避免,因为现实中朋友带来的便利往往少于带来的麻烦
当从objectivec++迈向templatec++时,这个条款就不一定适用了
总结: