第十三篇:成员函数与非成员函数的选择

前言

相信很多使用C++语言的人都有这么一种错误的观点 - 除了主函数,其他函数都应当声明为某个类的成员函数,以实现封装性。

这种观点错在哪里?我们又该如何在成员函数与非成员函数之间进行选择呢?

针对这个问题,本文将给出一种非常科学的解决方案。

问题分析

假定有一个网页浏览器类,其中有用来清除缓存,清除浏览记录,清除cookies的成员函数:

1 class WebBrowser
2 {
3 public:
4     // ......
5     void clearCache();    // 清除缓存
6     void clearHistory();    // 清除浏览记录
7     void removeCookies();    // 清除cookies
8     // ......
9 };

相信有很多用户希望能够一次执行完所有的这些函数。

故也许你会做出这样的设计:

 1 class WebBrowser
 2 {
 3 public:
 4     // ......
 5     void clearEverything();    // 这个函数包含下面三个函数
 6     void clearCache();    // 清除缓存
 7     void clearHistory();    // 清除浏览记录
 8     void removeCookies();    // 清除cookies
 9     // ......
10 };

但更有经验的程序员会用非成员函数实现此功能:

1 // 非成员函数
2 void clearBrowser(WebBrowser & wb)
3 {
4     wb.clearCache();
5     wb.clearHistory();
6     wb.removeCookies();
7 }

下面来分析这两种做法:

某个功能用成员函数来实现的话,对象中的变量将对该函数彻底开放,同时会将对象中的数据更大程度的暴露出来,尤其是随着开发的进行你会设置非常非常多的成员函数,到时你都搞不清哪些数据是封闭的哪些是可供用户操纵的。

故这样的做法很大程度上其实是破坏了封装性,应当采用下面这种做法。

也许你会抱怨,如果设置为非成员函数,那到时一堆非成员函数,代码岂非乱成一团?

那么你就需要好好学习下 - C++中非成员函数的使用方法了。

C++中非成员函数的使用方法

还是接着上面这个例子。

随着开发的进展,这个浏览器的类中会增加与书签有关的操作,与cookies相关的操作。。。。。。这种情况下,科学的做法是:

1. 浏览器类中仅仅包含最核心的一部分函数 - 也即所有的用户都需要的。

2. 对于书签与cookies相关操作,分别建立各自的.h,.cpp文件。

3. 将1,2中的所有头文件都囊括进一个namespace。这里设置namespace的目的就是为了防止非成员函数重名。关于namespace的相关说明请查阅相关文档。

具体实现可参考以下代码:

 1 // webbrowser.h
 2 namespace WebBrowserStuff {
 3
 4     class WebBrowser
 5     {
 6         // ......
 7         // 仅定义该类最核心的功能
 8         // ......
 9     };
10
11     // 定义核心非成员函数
12 }
13
14 // webbrowserbookmarks.h
15 namespace WebBrowserStuff {
16
17     // 定义与书签有关的非成员函数
18
19 }
20
21 // webbrowsercookies.h
22 namespace WebBrowserStuff {
23
24     // 定义与cookies有关的非成员函数
25
26 }

对于类的使用者来说,需要哪方面功能就 #include 哪个头文件,非常方便。

小结

如今一些大型类库,比如标准类库,OpenCV,都是这样的组成及使用方式。

以后的文章中会告诉你如何将你自己实现的模块封装成dll,供其他人使用,就像OpenCV OpenGL等知名类库一样,敬请期待:)

时间: 2024-10-16 02:39:27

第十三篇:成员函数与非成员函数的选择的相关文章

成员函数与非成员函数的选择

前言 相信很多使用C++语言的人都有这么一种错误的观点 - 除了主函数,其他函数都应当声明为某个类的成员函数,以实现封装性. 这种观点错在哪里?我们又该如何在成员函数与非成员函数之间进行选择呢? 针对这个问题,本文将给出一种非常科学的解决方案. 问题分析 假定有一个网页浏览器类,其中有用来清除缓存,清除浏览记录,清除cookies的成员函数: 1 class WebBrowser 2 { 3 public: 4 // ...... 5 void clearCache(); // 清除缓存 6 v

条款十九: 分清成员函数,非成员函数和友元函数

成员函数和非成员函数最大的区别在于成员函数可以是虚拟的而非成员函数不行.所以,如果有个函数必须进行动态绑定(见条款38),就要采用虚拟函数,而虚拟函数必定是某个类的成员函数.如果函数不必是虚拟的,情况就稍微复杂一点. 看下面表示有理数的一个类: class rational { public: rational(int numerator = 0, int denominator = 1); int numerator() const; int denominator() const; cons

c++运算符重载-如何决定作为成员函数还是非成员函数

The Decision between Member and Non-member The binary operators = (assignment), [] (array subscription), -> (member access), as well as the n-ary () (function call) operator, must always be implemented as member functions, because the syntax of the l

C++多态篇2——虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏

上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我在最后分析了虚函数与虚函数表的内存布局,在下一篇详细剖析虚函数及虚函数表的过程中,我发现有关函数重载,函数覆盖,函数重写和函数协变的知识也要理解清楚才能对虚函数表在内存中的布局,对派生类的对象模型以及对多态的实现有更深的理解. 所以这一篇我作为一篇过渡篇,也同时对我以前写过的一篇博文进行一个收尾.在C++继承详解之二--派生类成员函数详解(函数隐藏.构造函数与兼容覆盖规则)文章中,我对函数覆盖,重载,重写提了一下,但是没

More Effective C++----技巧 & (25)将构造函数和非成员函数虚拟化

技巧 本书涉及的大多数内容都是编程的指导准则.这些准则虽是重要的,但是程序员不能单靠准则生活.有一个很早以前的卡通片叫做"菲利猫"(Felix the Cat), 菲利猫无论何时遇到困难,它都会拿它的trick包.如果一个卡通角色都有一个trick包,那么C++程序员就更应该有了.把这一章想成你的trick包的启动器. 当设计C++软件时,总会再三地受到一些问题的困扰.你如何让构造函数和非成员函数具有虚拟函数的特点?你如何限制一个类的实例的数量?你如何防止在堆中建立对象呢?你如何又能确

c++ 如何把this指针传入成员函数 像全局函数一样调用成员函数

测试这个功能的初衷是测试boost里面的bind boost::bind((&A::sum), &a, _1, _2) 上面的代码是我boost bind及多线程这篇博客里面的一行代码.我就想boost是怎么做到这样调用一个类的成员函数的.其实成员函数和全局函数无非就是差一个this指针参数.给传进去不就也可以调用了.然而并没有那么简单.看了boost的源码表示太长了.没怎么看懂 然后就自己写代码测试了一下.还用了汇编.. 代码参考  http://www.cppblog.com/woai

拷贝构造,深度拷贝,关于delete和default相关的操作,explicit,类赋初值,构造函数和析构函数,成员函数和内联函数,关于内存存储,默认参数,静态函数和普通函数,const函数,友元

 1.拷贝构造 //拷贝构造的规则,有两种方式实现初始化. //1.一个是通过在后面:a(x),b(y)的方式实现初始化. //2.第二种初始化的方式是直接在构造方法里面实现初始化. 案例如下: #include<iostream> //如果声明已经定义,边不会生成 class classA { private: int a; int b; public: //拷贝构造的规则,有两种方式实现初始化 //1.一个是通过在后面:a(x),b(y)的方式实现初始化 //2.第二种初始化的方式是直

将构造函数以及非成员函数 “虚化”

虚构造函数,这似乎是很违反直观 的一件事情,因为当你拥有一个对象的指针或者引用的时候,你可以通过该对象的动态类型来调用虚函数,而在此处,你的对象还没有构造完成. 其实,"虚构造函数"并不是把你的构造函数声明为virtual,而是某一种函数,根据其输入数据,来决定此时构造什么类型的对象.比如你要从文件或者网络上读取一些数据,从该数据你来决定你要创建什么类型的对象. 比如: 下述的继承体系:[摘自More Effective C++ P124] class Base { public: .

读书笔记 effective c++ Item 46 如果想进行类型转换,在模板内部定义非成员函数

1. 问题的引入——将operator*模板化 Item 24中解释了为什么对于所有参数的隐式类型转换,只有非成员函数是合格的,并且使用了一个为Rational 类创建的operator*函数作为实例.在继续之前建议你先回顾一下这个例子,因为这个条款的讨论是对它的扩展,我们会对Item 24的实例做一些看上去无伤大雅的修改:对Rational和opeartor*同时进行模板化: 1 template<typename T> 2 class Rational { 3 public: 4 Rati