zoukankan      html  css  js  c++  java
  • 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这个接口的要求.

    总结:

      本文章介绍了如用利用模板的参数来实现接口,减少各个模块之间的耦合度,提高代码的重用性,减少了代码的冗余.我们可以看到,通过利用模板的技术,我们可以写出一个很简洁但是很通用的一个功能出来.



      

  • 相关阅读:
    element-ui Notification重叠问题,原因及解决办法
    详解CSS3实现无限循环的无缝滚动
    js监听浏览器离开页面操作
    判断浏览器
    轮播动效 | 环形进度条 -- 等 动效库
    拓扑插件搜集
    jquery-图片懒加载
    [原]开源的视频转换器,支持gpu,绝对好用ffmpeg的GUI==》dmMediaConverter最新版本2.3
    【原】font-awesome IE6支持代码本人测试成功
    【原创】 c#单文件绿色资源自更新
  • 原文地址:https://www.cnblogs.com/marchtea/p/2337606.html
Copyright © 2011-2022 走看看