前言
最近在看侯捷的一套课程《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
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/274804
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!