ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

条款23和条款24

2022-01-05 00:00:41  阅读:169  来源: 互联网

标签:24 ... 函数 23 member WebBrowser Rational class 条款


条款23:宁以non-member、non-friend替换member函数
为什么要用非成员函数、非友元函数替换成员函数呢?其实这是为了保证数据的封装性。而数据的封装性强弱是怎么体现的呢?一种粗糙的量测,我们认为越多的函数能访问它,数据的封装性就越低,因为如果数据发生改变,因它的改变牵扯到需要改变的太多,所以它的封装性较差。
而非成员函数、非友元函数较成员函数而言,访问不到private中的数据,数据的封装性自然就更好一些。也就是说,越多的函数可以访问,数据的封装性就越低。

需要注意的是,只因在意封装性而让函数“成为class的non-member”,并不意味它“不可以是另一个class的member”。该函数可以成为另一个类的static member函数,只要不影响原类中private成员的封装性。

看一下书中的例子:

class WebBrowser {
public:
	void clearCache();
	void clearHistory();
	void removeCookies();
}

现在有这样一个类,类中提供三种方法用来清除web浏览器的一些缓存。如果我们想一起清除Cache、History、Cookies,我们可以将这三个函数放在一个成员函数中去做,这样每次只需调用一个函数就行。

class WebBrowser{
public:
	void clearEverything(); //调用clearCache(),clearHistory(),removeCookies()
}

clearEverything()是一个成员函数,条款中不是说宁以non-member、non-friend替换member吗?因此,可以这样做:

void clearBrowser(WebBrowser &wb) {
	wb.clearCache();
	wb.clearHistory();
	wb.removeCookies();
}

让它成为一个非成员函数。
而在c++中我们通常可以将类和有关该类的非成员函数放在同一命名空间中,就像这样:

namespace WebBrowserStuff {
	class WebBrowser {...};
	void clearBrowser(WebBrowser& wb);
	...
}

同时,与WebBrowser类有关的便利函数,可能有多个,以及多个种类,比如,与cookie相关的,还有与书签相关的等等。为了减少没必要的依赖(比如,我现在只想用与cookie相关的函数,也就没必要引入与书签相关的函数),我们可以将他们分别声明在不同的头文件中,但都属于WebBrowserStuff这一命名空间,因为,他们都是与WebBrowser有关的。

//头文件“webbrowser.h”这个头文件针对class WebBrowser自身及WebBrowser核心机能。
namespace WebBrowserStuff {
	class WebBrowser {...};
	... 	//核心机能,例如几乎所有客户都需要的non-member函数
}

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

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

将便利函数放在多个头文件中但都属于同一命名空间,意味着我们可以轻松的在此基础上增加其他的便利函数,这也是命名空间较class好的一点,class是一个整体,对于用户来说不能扩展,而命名空间可以。

条款24:若所有参数皆需类型转化,请为此采用non-member函数
现在我们定义一个有理数的类:

class Rational {
public:
	Rational(int numerator = 0, int denominator = 1);
	int numerator() const;
	int denominator() const;
private:
	...
}

有理数相乘似乎是很正常的事,为此,在类中重载一下乘法运算:

class Rational {
public:
	...
	const Rational operator* (const Rational &rhs) const;
}

这样便可支持两个有理数相乘:

Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf * oneEighth;
result = result * oneEighth;

让有理数和整数相乘似乎也是很正常的事,当我们执行:

result = oneHalf * 2;//正确
result = 2 * oneHalf;//错误

可以发现,其中一个报错,这是因为,oneHalf是一个内含operator*函数的class的对象,所以编译器调用该函数。而整数2没有相应的class,也就没有operator*成员函数。编译器尝试非成员函数operator*(也就是在命名空间内或在global作用域内),然而其他地方也没有相关定义,因此报错。再来看result = oneHalf * 2,这个为什么正确呢?其实这里涉及到一个隐式的类型转换,即2通过构造函数转换为一个Rational类型,两个Rational类型相乘,自然没有报错。
只有当参数被列于参数列内,这个参数才是隐式类型转换的合格参与者,正如result = oneHalf * 2一样。对于出错的情况,我们可以重载一个非成员函数的乘法运算:

class Rational {
	...
};
const Rational operator*(const Rational& lhs, const Rational& rhs) {
	return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());
}

其实上述两种重载方式称为类内重载和类外重载,当两种重载方式同时存在时,编译器会优先使用类内重载,因为类内重载对于类的使用者来说是优先可见的,因此它的优先级更高(对于类的使用者而言,优先看到的肯定是意图中想用的)。

标签:24,...,函数,23,member,WebBrowser,Rational,class,条款
来源: https://blog.csdn.net/gaoyuelon/article/details/121884258

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有