1. 程式人生 > >侯捷《C++面向對象開發》——動手實現自己的復數類

侯捷《C++面向對象開發》——動手實現自己的復數類

using ble 養成 參數類型 打破 什麽 自己 ret polar

前言

最近在看侯捷的一套課程《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 }

總結

作為初學者,一定要養成良好的編程習慣,正如侯捷所說:“一出手就是大家風範”。

侯捷《C++面向對象開發》——動手實現自己的復數類