类的设计(四) 作为接口的模板与泛型算法

解决问题的过程就是数据抽象的过程。对于一个函数来说,它的任务是为了完成某个特定的工作,通常我们称为功能模块。注意是特定。通常模块化是有效的,它可以更好的提高效率。那么抽象模块化可能就相当于意味着使工作不再变得特定。它将变得更加通用(也就是进一步抽象)。打个例子,这里有一个数组求和的代码,以便思考,可以运用到其他地方(例子会越来越抽象):

int sum(int *  p,int n)
{
     int Sum = 0;
     for (int i = 0; i < n; ++i)
     {
          Sum += *p;
          ++p;
     }
     return Sum;
}

简单的是为这一数组求和。但是想想有什么思想能把例子更抽象化,可以适应很多类型,所以很容易想到模板:

template<class T>
T sum(T * data, int n)
{
     T result = 0;
     for (int i = 0; i < n; ++i)
          result += data[i];

     return result;
}

这里抽象了类型,也就是说只要这个T类,它只要满足以下三种操作,那么便可以使用这个求和函数:

1.以0作为对象

2.支持 [ ] 操作符

3.重载了 += 操作符

仔细想想,这个函数还是不够灵活....

这里sum函数知道3件事情:

1.它把一组数加在一起

2.它所加的数是整数,小数等(内置类型-int double float unsigned int等)

3.它所加的整数是一种特殊的方式存储了起来(数组)

那么当然,可以重写这个sum函数。

可以先分离迭代方式

 要从sum函数中移走的第一个东西   ----   添加的元素是放在数组中,我们可以试着摆脱这种限制

可以用类来抽象这种依赖:(所以也可以容易的写出)

tempalate<class T>
class iterator
{
public:
     //构造函数有两个参数,第一个元素的地址,序列的长度
     iterator(T * p, int c) :data(p), len(c) {}
     ~iterator() {}

     //如果序列中还有元素存在,则成员valid返回一直非零值
     int valid() const { return len > 0; }
     //取出下一个元素。只有序列中有值的情况,才会调用此函数
     int next() {
          --len;
          return *data++;
     }
     iterator(const iterator&) {}
     iterator& operator = (const iterator&) {}
private:
     T * data;
     int len;

};

那么相应的sum求和函数可以进一步简化:

template<class T>
T sum(iterator<T> ir)
{
     T result = 0;
     while (ir.valid())
          result += ir.next();
     return result;
}

虽然看起来sum对于它要添加的值所知道的唯一事情,就是参数是iterator类型,sum内部逻辑并不怎么依赖于iterator类,它只使用valid()跟next()操作。那么有什么办法使sum运用到那些并不是以数组形式存储的序列上?

所需要的是用来反映不同数据结构的不同迭代器,到现在为止,我们只能访问数组中的值。假如数据保存在链表中,或者保存在文件中该怎么办?有没有办法进一步抽象化?我们可以进一步抽象这个类(可以表示许多不同迭代器类中的任何一个):

 template<class T>
     class Iterator {

     public:
          virtual int valid() const = 0;
          virtual T next() = 0;
          virtual ~Iterator() {}
     };

然后这里:可以认为数组类型为Array_iterator<T>是一种iterator<T>,这里用了继承

//可以认为数组访问 ----Array_iterator<T>是一种iterator<T>
     template<class T> class Array_Iterator :public Iterator<T>
     {
     public:
          Array_Iterator(T * p, int c) :data(p), len(c) {}
          int valid() const { return len > 0; }
          T next() {
               --len;
               return *data++;
          }

     private:
          T * data;
          int len;
     };

然后我们再一次修改sum函数:(这里说明下,因为用到动态绑定,所以这里形参要传引用的方式)

template<class T>
T sum(iterator<T>& ir)
{
     T result = 0;
     while (ir.valid())
          result += ir.next();
     return result;
}

那么我们可以这样:

int main()
{
     //求和就变成这样
     int  x[10];
     for (int i = 0; i < 10; ++i)
          x[i] = i;
     //与前面的例子扩展相比,这种开销可能是昂贵的。,因为有虚函数

     //能不能弥补动态绑定带来的效率问题?
     //取消动态绑定???
     //那就意味着在编译时候,我们需要知道集合的类型
     Array_Iterator<int> it(x, 10);
     cout << sum(it) << endl;
     return 0;
}

到了这一步,我们是否还要把动态绑定的效率问题考虑进去??网上有些人说不用(在这里,我们先不纠结是否考虑,默认是考虑),但是也是一种思想,这里给出了解决方案。

首先我们可以取消动态绑定--这意味着在编译的时候要知道这个集合的类型 也就是知道Array_iterator类型

我们可以再一次利用模板的特性:

这里让模板接受两个参数

这里Iter --- Array_iterator,让编译器在编译的时候就确定Array_iterator类型

template<class Iter, class T> T sum(T& result,Iter ir)
{
     while (ir.valid())
          result += ir.next();
     return result;
}

当然为了说明这种方法的灵活性,可以利用sum对一个从istream中生成的,数量不限的数字集合来求和。

首先需要一个当作迭代器的类。当然,这个类跟之前Array_iterator的类有着明显的差别。这里称为Reader.

template<class T> class Reader
     {
         public:
              Reader(istream& is) :i(is) { advance(); }
              int valid() const { return status; }
              T next() {
                   T result = 0;
                   advance();
                   return result;
              }
         private:
              istream& i;
              int status;
              T data;
              void advance()
              {
                   i >> data;
                   status = i != 0;
              }
     };

注意这里不同于以往,没有使用继承,因为动态绑定被取消了,编译器在编译的时候就已经确定了类型,所以动态绑定的存在变得不再有意义。

int main()
{
     cout << "输入数字:" << endl;
     double r = 0;
     sum(r, Reader<double>(cin));
     return 0;
}

当然这种设计忽略了很多细节,比如可以把sum的参数改成迭代器,然后抽象迭代器,让迭代器提供统一的数据访问接口。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值