侯捷《C++面向对象开发》——动手实现自己的复数类

前言

最近在看侯捷的一套课程《C++面向对象开发》,刚看完第一节introduction之后就被疯狂圈粉。感觉侯捷所提及所重视的部分也正是我一知半解的知识盲区,我之前也写过一些C++面向对象的程序,不过正如侯捷所说,我还仅仅停留于Object-based层面,写程序时总是在想如何封装好一个类,而不是Object-oriented强调类与类之间关系的设计。

这门课程分为两部分,第一部分讲Object-based,第二部分讲Object-oriented;第一部分又分为两部分:带指针的类的封装和不带指针类的封装。

本文将以模板库中的complx复数类的部分内容为核心,在分析源代码的同时,讲解一些良好的代码风格和编程习惯,比如inline内联函数的使用、friend友元函数的使用、函数参数及返回值何时pass by value何时pass by reference等等。

部分代码

complex.h

 1 #ifndef __COMPLEX__
 2 #define __COMPLEX__
 3
 4 class complex
 5 {
 6     public:
 7         complex(double r = 0, double i = 0)
 8             : re (r), im (i)
 9         { }
10         complex& operator += (const complex&);
11         double real () const { return re; }
12         double imag () const { return im; }
13     private:
14         double re, im;
15
16         friend complex& __doapl (complex*, const complex&);
17 };
18
19 #endif

complex.cpp

 1 #include "complex.h"
 2 #include <iostream>
 3
 4 using namespace std;
 5
 6 inline complex& __doapl(complex* ths, const complex& r)
 7 {
 8     ths->re += r.re;
 9     ths->im += r.im;
10     return *ths;
11 }
12
13 inline complex& complex::operator += (const complex& r)
14 {
15     return __doapl (this, r);
16 }
17
18 inline double imag (const complex& x)
19 {
20   return x.imag ();
21 }
22
23 inline double real (const complex& x)
24 {
25   return x.real ();
26 }
27
28 inline complex operator + (const complex& x, const complex& y)
29 {
30     return complex (real (x) + real (y), imag (x) + imag (y));
31 }
32
33 inline complex operator + (const complex& x, double y)
34 {
35     return complex (real (x) + y, imag (x));
36 }
37
38 inline complex operator + (double x, const complex& y)
39 {
40     return complex (x + real (y), imag (y));
41 }
42
43 ostream& operator << (ostream& os, const complex& x)
44 {
45     return os << ‘ (‘ << real (x) << "," << imag (x) << ‘)‘;
46 }

源码解析

一、complex.h

1.1 initialization list

         //程序1.1
         complex(double r = 0, double i = 0)
             : re (r), im (i)
         { }

构造函数参数缺省,比较常规。

值得注意的是,变量的初始化尽量放在初始化列表中(initialization list)。当然,完全可以在构造函数的函数体中赋值进行初始化。不过,侯捷指出,一个对象在产生过程中分为初始化和成功产生两部分,initialization list相当于在初始化过程中对变量赋值,而在函数体中赋值则是放弃了initialization list初始化这一过程,会降低效率。对于“性能榨汁机”的C++语言来讲,重视每个细节效率的重要性是毫无疑问的。

1.2参数及返回值传递方式

        //程序1.2 2
        complex& operator += (const complex&);

传递参数时,如果能用引用传递那么一定不要用值传递,因为值传递的过程中变量需要copy一份样本传入函数中,当参数很多或参数类型复杂时,会导致效率变慢。

其次,如果函数不会改变参数的值,一定要加const限定,在初学时养成良好的变成习惯尤为重要。

关于函数的返回值,同样是最好按引用传递,当然,有些情况无法按引用传递,这点将在2.3讲解。

其实,参数列表中还隐藏一个this,这点将在2.2讲解。

1.3友元函数

       //程序1.3
       friend complex& __doapl (complex*, const complex&);

我们可以看到,在complex.h文件的末尾定义了一个友元函数,友元函数打破了类的封装,它不是类的成员函数,却可以使用点操作符来获取类的private变量。当然,非友元函数也可以通过get函数来获取,不过速度会慢一些。

二、complex.cpp

2.1 友元函数及内联函数

//程序2.1
inline complex& __doapl(complex* ths, const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;
}

我们首先来分析一下这个友元函数,这里有两点值得探讨:

第一这个函数将r的实部和虚部加到ths上,r在函数体中值没用发生改变,所以使用const限定。

第二这个函数被设计成inline内联函数,我们都知道,内联函数是把代码块直接复制到函数需要调用的地方,通过省略函数调用这一过程来提高效率,那么我们为什么不将所有函数都设计成内联函数呢?其实我们的inline声明只是对编译器的一个建议,对于过于复杂的函数来讲,及时我们声明了inline,编译器也会调用执行。所以,对于一些“小巧”的函数,我们尽量设计为内联函数。

2.2 隐藏的“this”

//程序2.2
inline complex& complex::operator += (const complex& r)
{
    return __doapl (this, r);
}

操作符重载作为C++的特点之一,有令别的语言羡慕之处,当然也有些难以理解。

实际上,这个函数的参数还有一个隐藏的this,这个this就是函数调用者。

2.3 不能为reference的返回值

//程序2.3
inline complex operator + (const complex& x, const complex& y)
{
    return complex (real (x) + real (y), imag (x) + imag (y));
}

inline complex operator + (const complex& x, double y)
{
    return complex (real (x) + y, imag (x));
}

inline complex operator + (double x, const complex& y)
{
    return complex (x + real (y), imag (y));
}

注意,这里函数的返回值不能返回reference,这其实是使用临时对象(typename ()),在函数体内定义变量,然后把这个变量的引用传递出去,函数结束后变量本体死亡,传出去的引用既没有意义了。

2.4 非成员函数的操作符重载

//程序2.4
ostream& operator << (ostream& os, const complex& x)
{
    return os << ‘ (‘ << real (x) << "," << imag (x) << ‘)‘;
}

下面讲一下为什么有的操作符重载函数定义成非成员函数?

我们知道,操作符重载只作用在左边的操作数上,试想一下,如果把“<<”定义为成员函数,那每次调用岂不是要这样c1 << cout

完整代码

以上就是我在学习过程中特别注意的地方,下面给出complex类完整代码,只不过多了几种操作运算,大体思路完全一致。

complex.h

  1 #ifndef __MYCOMPLEX__
  2 #define __MYCOMPLEX__
  3
  4 class complex;
  5 complex&
  6   __doapl (complex* ths, const complex& r);
  7 complex&
  8   __doami (complex* ths, const complex& r);
  9 complex&
 10   __doaml (complex* ths, const complex& r);
 11
 12
 13 class complex
 14 {
 15 public:
 16   complex (double r = 0, double i = 0): re (r), im (i) { }
 17   complex& operator += (const complex&);
 18   complex& operator -= (const complex&);
 19   complex& operator *= (const complex&);
 20   complex& operator /= (const complex&);
 21   double real () const { return re; }
 22   double imag () const { return im; }
 23 private:
 24   double re, im;
 25
 26   friend complex& __doapl (complex *, const complex&);
 27   friend complex& __doami (complex *, const complex&);
 28   friend complex& __doaml (complex *, const complex&);
 29 };
 30
 31
 32 inline complex&
 33 __doapl (complex* ths, const complex& r)
 34 {
 35   ths->re += r.re;
 36   ths->im += r.im;
 37   return *ths;
 38 }
 39
 40 inline complex&
 41 complex::operator += (const complex& r)
 42 {
 43   return __doapl (this, r);
 44 }
 45
 46 inline complex&
 47 __doami (complex* ths, const complex& r)
 48 {
 49   ths->re -= r.re;
 50   ths->im -= r.im;
 51   return *ths;
 52 }
 53
 54 inline complex&
 55 complex::operator -= (const complex& r)
 56 {
 57   return __doami (this, r);
 58 }
 59
 60 inline complex&
 61 __doaml (complex* ths, const complex& r)
 62 {
 63   double f = ths->re * r.re - ths->im * r.im;
 64   ths->im = ths->re * r.im + ths->im * r.re;
 65   ths->re = f;
 66   return *ths;
 67 }
 68
 69 inline complex&
 70 complex::operator *= (const complex& r)
 71 {
 72   return __doaml (this, r);
 73 }
 74
 75 inline double
 76 imag (const complex& x)
 77 {
 78   return x.imag ();
 79 }
 80
 81 inline double
 82 real (const complex& x)
 83 {
 84   return x.real ();
 85 }
 86
 87 inline complex
 88 operator + (const complex& x, const complex& y)
 89 {
 90   return complex (real (x) + real (y), imag (x) + imag (y));
 91 }
 92
 93 inline complex
 94 operator + (const complex& x, double y)
 95 {
 96   return complex (real (x) + y, imag (x));
 97 }
 98
 99 inline complex
100 operator + (double x, const complex& y)
101 {
102   return complex (x + real (y), imag (y));
103 }
104
105 inline complex
106 operator - (const complex& x, const complex& y)
107 {
108   return complex (real (x) - real (y), imag (x) - imag (y));
109 }
110
111 inline complex
112 operator - (const complex& x, double y)
113 {
114   return complex (real (x) - y, imag (x));
115 }
116
117 inline complex
118 operator - (double x, const complex& y)
119 {
120   return complex (x - real (y), - imag (y));
121 }
122
123 inline complex
124 operator * (const complex& x, const complex& y)
125 {
126   return complex (real (x) * real (y) - imag (x) * imag (y),
127                real (x) * imag (y) + imag (x) * real (y));
128 }
129
130 inline complex
131 operator * (const complex& x, double y)
132 {
133   return complex (real (x) * y, imag (x) * y);
134 }
135
136 inline complex
137 operator * (double x, const complex& y)
138 {
139   return complex (x * real (y), x * imag (y));
140 }
141
142 complex
143 operator / (const complex& x, double y)
144 {
145   return complex (real (x) / y, imag (x) / y);
146 }
147
148 inline complex
149 operator + (const complex& x)
150 {
151   return x;
152 }
153
154 inline complex
155 operator - (const complex& x)
156 {
157   return complex (-real (x), -imag (x));
158 }
159
160 inline bool
161 operator == (const complex& x, const complex& y)
162 {
163   return real (x) == real (y) && imag (x) == imag (y);
164 }
165
166 inline bool
167 operator == (const complex& x, double y)
168 {
169   return real (x) == y && imag (x) == 0;
170 }
171
172 inline bool
173 operator == (double x, const complex& y)
174 {
175   return x == real (y) && imag (y) == 0;
176 }
177
178 inline bool
179 operator != (const complex& x, const complex& y)
180 {
181   return real (x) != real (y) || imag (x) != imag (y);
182 }
183
184 inline bool
185 operator != (const complex& x, double y)
186 {
187   return real (x) != y || imag (x) != 0;
188 }
189
190 inline bool
191 operator != (double x, const complex& y)
192 {
193   return x != real (y) || imag (y) != 0;
194 }
195
196 #include <cmath>
197
198 inline complex
199 polar (double r, double t)
200 {
201   return complex (r * cos (t), r * sin (t));
202 }
203
204 inline complex
205 conj (const complex& x)
206 {
207   return complex (real (x), -imag (x));
208 }
209
210 inline double
211 norm (const complex& x)
212 {
213   return real (x) * real (x) + imag (x) * imag (x);
214 }
215
216 ostream&
217 operator << (ostream& os, const complex& x)
218 {
219   return os << ‘(‘ << real (x) << ‘,‘ << imag (x) << ‘)‘;
220 }
221
222 #endif   //__MYCOMPLEX__

complex_test.cpp

 1 #include <iostream>
 2 #include "complex.h"
 3
 4 using namespace std;
 5
 6 int main()
 7 {
 8   complex c1(2, 1);
 9   complex c2(4, 0);
10
11   cout << c1 << endl;
12   cout << c2 << endl;
13
14   cout << c1+c2 << endl;
15   cout << c1-c2 << endl;
16   cout << c1*c2 << endl;
17   cout << c1 / 2 << endl;
18
19   cout << conj(c1) << endl;
20   cout << norm(c1) << endl;
21   cout << polar(10,4) << endl;
22
23   cout << (c1 += c2) << endl;
24
25   cout << (c1 == c2) << endl;
26   cout << (c1 != c2) << endl;
27   cout << +c2 << endl;
28   cout << -c2 << endl;
29
30   cout << (c2 - 2) << endl;
31   cout << (5 + c2) << endl;
32
33   return 0;
34 }

总结

作为初学者,一定要养成良好的编程习惯,正如侯捷所说:“一出手就是大家风范”。

原文地址:https://www.cnblogs.com/henuzyx/p/9107842.html

时间: 2024-07-29 03:47:39

侯捷《C++面向对象开发》——动手实现自己的复数类的相关文章

侯捷C++面向对象高级编程

原文地址:https://www.cnblogs.com/zonkidd/p/12253026.html

面向对象开发C++快速入门视频教程 C++基础加实战视频教程

课程目录: ├<C++面向对象高级开发(上)> │ ├1.C++编程简介.mp4 │ ├2.头文件与类的声明.mp4 │ ├3.构造函数.mp4 │ ├4.参数传递与返回值.mp4 │ ├5.操作符重载与临时对象.mp4 │ ├6.复习Complex类的实现过程.mp4 │ ├7.三大函数:拷贝构造,拷贝复制,析构.mp4 │ ├8.堆,栈与内存管理.mp4 │ ├9.复习String类的实现过程.mp4 │ ├10.扩展补充:类模板,函数模板,及其他.mp4 │ ├11.组合与继承.mp4

侯捷访谈录摘抄-3

1.理解操作系统内部运作及各种规范与协议的基础层面 2.沉淀历练+扎实的基础才能浮升,开始抽象的思考,进行高层次的开发 3.任何工程领域之前身(或背后)一定有其科学研究或尖端研究 4.业界要的是持续稳定,保证投资和优势,MFC保持向后的兼容性也在考验着微软的智能 5.MFC的宏,自制的RTTI机制,完全符合C++标准 6.基础学问如同万古长空,IDE如一朝风月 7.不同的场合运用不同的技术才能红花绿叶相辅相成威力加乘 8.从大型卓越作品中汲取养分 9.Application Framework:

新手如何理解JS面向对象开发?

今天有时间讲讲我对面向对象的理解跟看法,尽量用通俗的语言来表达,多多指教! 如今前端开发已经越来越火了,对于前端开发的要求也是越来越高了,在面试中,经常有面试官会问:你对JS面向对象熟悉吗? 其实,也就是相当于在问你,在工作中有没有用过面向对象开发?说到这里,有人就问了,什么事面向对象? 面向对象: 用我个人最简单的理解表达就是,Object的操作.另外一种理解: 给你一个条件,你去找个对象帮我处理这个事情,你就不要自己动手了. 说白了,我们就是在操作对象,那么我们就需要去创建这个对象,创建对象

王家林的云计算分布式大数据Hadoop企业级开发动手实践

一:课程简介: Hadoop是云计算分布式大数据的事实标准软件框架,Hadoop中的架构实现是整个云计算产业技术的基础,作为与Google三大核心技术DFS.MapReduce.BigTable相对的HDFS.MapReduce.和HBase也是整个Hadoop生态系统的核心的技术,本课程致力于帮您掌握这三大技术的同时掌握云计算的数据仓库挖掘技术Hive,助您在云计算技术时代自由翱翔. 二:课程特色 1,      深入浅出中动手实作: 2,      掌握Hadoop三大核心:HDFS.Map

结构化系统开发和面向对象开发方法

结构化系统开发方法: 系统分析员.软件工程师.程序员以及最终用户按照用户至上的原则,自顶向下分析与设计和自底向上逐步实施的建立计算机信息系统的一个过程,是组织.管理和控制信息系统开发过程的一种基本框架. 三部分:管理策略部分:强调系统开发的规划.进程安排.评估.监控和反馈.开发策略部分:任务分解结构:WBS优先级结构.开发经验.开发标准. 开发过程分为:系统规划阶段.系统分析阶段.系统设计阶段.系统实施阶段.系统运行与维护阶段结构化开发早期的程序开发,如C语言,都是用结构化开发方法. 面向对象开

侯捷访谈录摘抄-2

1.未有天才之前,应先营造天才的土壤 2.花的是自己辛苦挣来的钱,千万不要浪费在无用的东西上 3.良性循环的一个体系的建制,需从底层到顶层的坚实构筑 4.寻常一样窗前月,才有梅花便不同 5.所有同质的技术都有积累性和共同性 6.RAD的高手,无一不是有底层深厚的功力 7.基础知识的精通,是作为应用的一种过程和手段,而不是目的 8.反复读好书 9.研究Qt 10.会用STL-了解STL原理-追踪STL源码(用起来虎虎生威!) 侯捷访谈录摘抄-2,布布扣,bubuko.com

php面向对象开发的学习

1.概念:程序设计中采用封装,继承,抽象等设计方式. 2.传统开发问题 软件重用性差.可维护性差,无很好扩展性 3.要素 抽象性,封装性,共享性,强调对象结构而不是程序 4.特点(缺一不可) 封装(把相似类型封装成一类),继承(父类有什么属性,子类就有什么属性),多态(运行时加载) 5.类:一个功能的集合菜单(用户不同,需求不同) 6.创建类:class 方法名{...} 例:class Student{     public $id;    public $name;    public $c

侯捷访谈录摘抄-4

1.推荐的书目 Inside Visual C++ (4th) Programming Windows 95 with MFC MFC Internals 2.DDK:撰写驱动程序(DRV)或虚拟设备驱动器(VxD),需要理解操作系统(汇编.C的天下) 3.No touch,no chance 4.提升基本功(语言能力,操作系统.数据结构.演算分析...).组织能力和文字能力 5.整理MSJ.DDJ和WDJ期刊 6.前途在自己手里 7.一台PC,可以把自己锻炼成百战金刚 8.急功近利是大忌 9.