大家好.由於七七八八的原因給耽擱了,好久好久沒更新BLOG了.
現在繼續更新我的閱讀C++沉思錄的筆記.
本篇博文是我閱讀沉思錄第16章的筆記.在這篇博文中,主要講了C++中接口的技術.
問題的提出:
總所周知,JAVA和C#都有很簡單的接口機制.其實現是通過了關鍵字interface來實現,也即類似下列代碼
public interface Test { public String getStr(); }
這個方式是聲明了一個接口類.然后由代碼實現這個接口.
不過本文說的接口和此接口不相同.JAVA的這種接口可以由C++的純虛函數來實現.本文說的是基於模板的接口函數的技術.
一個簡單的問題:
對一個數組進行求和.返回結果.
自然而然,我們會編寫一個函數叫sum.由於我們已經知道數組長度,數組類型,我們寫出的代碼很可能就是這個樣子.
1 #include <iostream>
2 using namespace std;
3
4 //sum v1
5 int sum(int *array,int num)
6 {
7 int result = 0;
8 for(int i = 0; i != num;++i)
9 result += array[i];
10 return result;
11 }
12
13 int main()
14 {
15 int *d = new int[10];
16 for(int i = 0;i!=10;++i)
17 d[i] = i + 1;
18 cout << sum(d,10) << endl;
19 }
這個函數沒有任何錯誤,工作的也很好.不過,要是這時候要求對一個double型的數組累加呢?不可能再寫一遍一模一樣只是類型不同的函數吧?
顯然我們要做的就是進行模板化.利用模板來實現累加任何類型.
不過在此之前,還有一個問題,這里限定了其存儲方式必須是數組.而實際上,我們的sum的要求僅僅只有兩個.
1.可以進行遍歷,獲取值
2.知道何時結束.
而迭代器(Iterator)完全就可以符合這兩樣的要求.(Iterator之前沒講,我會盡快補上)
這個迭代器的要就是訪問下一個,判斷何時結束.因此,我們的第二個版本sum,也就是比較通用的版本就出來了.
1 #include <iostream>
2
3 using namespace std;
4
5 template<class T>
6 class Iterator {
7 public:
8 Iterator(T*,int);
9 bool validate()const;
10 T next();
11 private:
12 T *data;
13 int len;
14 };
15
16 template<class T>
17 Iterator<T>::Iterator(T *d,int l)
18 :data(d),len(l)
19 {
20
21 }
22
23 template<class T>
24 bool Iterator<T>::validate() const
25 {
26 return len > 0;
27 }
28
29 template<class T>
30 T Iterator<T>::next()
31 {
32 --len;
33 return *data++;
34 }
35
36 template<class T>
37 T sum(Iterator<T> ite)
38 {
39 T t = 0;
40 while(ite.validate())
41 t += ite.next();
42 return t;
43 }
44
45 int main()
46 {
47 int *d = new int[10];
48 for(int i = 0;i!=10;++i)
49 d[i] = i;
50 cout << sum(Iterator<int>(d,10)) << endl;
51 return 0;
52 }
不過問題又來了,我們的Iterator其實就是一個數組指針的變形.如果要為此再提供鏈表迭代器,那我們目前的就不符合了.因此,我們利用繼承的技術,對迭代器進行了抽象.
通過將Iterator設為純虛函數,然后繼承實現對於數組和鏈表的迭代器.代碼如下(只有數組,鏈表的同理可以實現)
1 #include <iostream>
2
3 using namespace std;
4
5 template<class T>
6 class Iterator
7 {
8 public:
9 virtual bool validate()const = 0;
10 virtual T next() = 0;
11 };
12
13 template<class T>
14 class Array_Iterator : public Iterator<T>
15 {
16 public:
17 Array_Iterator(T *d,int);
18 bool validate() const;
19 T next();
20 private:
21 T *data;
22 int len;
23 };
24
25 template<class T>
26 Array_Iterator<T>::Array_Iterator(T *d,int l)
27 :data(d),len(l)
28 {}
29
30 template<class T>
31 bool Array_Iterator<T>::validate()const{
32 return len > 0;
33 }
34
35 template<class T>
36 T Array_Iterator<T>::next()
37 {
38 --len;
39 return *data++;
40 }
41
42 template<class T>
43 T sum(Iterator<T> &it)
44 {
45 T result = 0;
46 while(it.validate())
47 result += it.next();
48 return result;
49 }
50
51 int main()
52 {
53 int *d = new int[10];
54 for(int i = 0;i!=10;++i)
55 d[i] = i;
56 Array_Iterator<int> ai(d,10);
57 cout << sum(ai) << endl;
58 delete d;
59 return 0;
60 }
恩.看起來我們的迭代器和sum函數已經很通用了.也許我們到達這一步就可以了.
不過有一個很現實的問題擺在所有C++程序員的面前.這東西效率怎么樣??
我想大家應該都知道,C++的虛函數使用其實是需要很大的代價的.無論從空間效率上來看也好,從時間效率上來看也好,虛函數動態綁定都是一個很昂貴的操作.實際上,STL里面的iterator也沒有采用繼承的方式.(至於成什么樣,之后的文章會詳細述說)因此,我們不采用這種方式.
我們采用的方式是
1 template<class T,class Ite>
2 void sum2(T& result,Ite ir)
3 {
4 result = 0;
5 while(ir.valid())
6 result += ir.next();
7 }
在我們的模板中,有兩個模板參數,一個是返回的值,一個是我們迭代器的類型.
只要對我們的main函數進行小小的修改,就可以運行了.
為了驗證我們設計的好處,這里,我們設計一個從istream里面獲取值進行累加的例子.sum2仍然不變,我們僅僅通過改變Ite即可實現此功能.
1 #include <iostream>
2
3 using namespace std;
4
5 template<class T>
6 class Reader{
7 public:
8 Reader(istream &is): i(is) {advance();}
9 int valid() const {return status;}
10 T next()
11 {
12 T result = data;
13 advance();
14 return result;
15 }
16 public:
17 istream& i;
18 bool status;
19 T data;
20 void advance()
21 {
22 i >> data;
23 status = i != 0;
24 }
25 };
26
27 template<class T,class Ite>
28 void sum2(T& result,Ite ir)
29 {
30 result = 0;
31 while(ir.valid())
32 result += ir.next();
33 }
34
35 int main()
36 {
37 cout << "Enter Number:" << endl;
38 double r = 0;
39 sum2(r,Reader<double>(cin));
40 cout << r << endl;
41 return 0;
42 }
顯然,我們不用對原來的代碼做大的修改,甚至完全不用修改sum2的代碼,只要Ite這個模板參數能進行valid和next操作,我們的函數就能正確的運行.而這正是sum2這個接口的要求.
總結:
本文章介紹了如用利用模板的參數來實現接口,減少各個模塊之間的耦合度,提高代碼的重用性,減少了代碼的冗余.我們可以看到,通過利用模板的技術,我們可以寫出一個很簡潔但是很通用的一個功能出來.