C++ 接口
大家好.由于七七八八的原因给耽搁了,好久好久没更新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这个接口的要求.
总结:
本文章介绍了如用利用模板的参数来实现接口,减少各个模块之间的耦合度,提高代码的重用性,减少了代码的冗余.我们可以看到,通过利用模板的技术,我们可以写出一个很简洁但是很通用的一个功能出来.