• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++实现 vector 的四则运算

C++实现 vector 的四则运算

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c++中vector的用法,c++中vector,c++容器vector,c++ vector,求c++中vector用法等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

这里假设 vector 的运算定义为对操作数 vector 中相同位置的元素进行运算,最后得到一个新的 vector。具体来说就是,假如 vector<int> d1{1, 2, 3}, d2{4, 5, 6};则, v1 + v2 等于 {5, 7, 9}。实现这样的运算看起来并不是很难,一个非常直观的做法如下所示:

vector<int> operator+(const vector<int>& v1, const vector<int>& v2) {
  // 假设 v1.size() == v2.size()
  vector<int> r;

  r.reserve(v1.size());
  for (auto i = 0; i < v1.size(); ++i) {
    r.push_back(v1[i] + v2[i]);
  }
  return r;
}

</div>

// 同理,需要重载其它运算符
我们针对 vector 重载了每种运算符,这样一来,vector 的运算就与一般简单类型无异,实现也很直白明了,但显然这个直白的做法有一个严重的问题:效率不高。效率不高的原因在于整个运算过程中,每一步的运算都产生了中间结果,而中间结果是个 vector,因此每次都要分配内存,如果参与运算的 vector 比较大,然后运算又比较长的话,效率会比较低,有没有更好的做法呢?

既然每次运算产生中间结果会导致效率问题,那能不能优化掉中间结果?回过头来看,这种 vector 的加减乘除与普通四则运算并无太大差异,在编译原理中,对这类表达式进行求值通常可以通过先把表达式转为一棵树,然后通过遍历这棵树来得到最后的结果,结果的计算是一次性完成的,并不需要保存中间状态,比如对于表达式:v1 + v2 * v3,我们通常可以先将其转化为如下样子的树:

因此求值就变成一次简单的中序遍历,那么我们的 vector 运算是否也可以这样做呢?

表达式模板

要把中间结果去掉,关键是要推迟对表达式的求值,但 c++ 不支持 lazy evaluation,因此需要想办法把表达式的这些中间步骤以及状态,用一个轻量的对象保存起来,具体来说,就是需要能够将表达式的中间步骤的操作数以及操作类型封装起来,以便在需要时能动态的执行这些运算得到结果,为此需要定义类似如下这样一个类:

enum OpType {
  OT_ADD,
  OT_SUB,
  OT_MUL,
  OT_DIV,
};

class VecTmp {
  int type_;
  const vector<int>& op1_;
  const vector<int>& op2_;

public:
  VecTmp(int type, const vector<int>& op1, const vector<int>& op2)
    : type_(type), op1_(op1), op2_(op2) {}

  int operator[](const int i) const {
    switch(type_) {
      case OT_ADD: return op1_[i] + op2_[i];
      case OT_SUB: return op1_[i] - op2_[i];
      case OT_MUL: return op1_[i] * op2_[i];
      case OT_DIV: return op1_[i] / op2_[i];
      default: throw "bad type";
    }
  }
};

</div>

有了这个类,我们就可以把一个简单的运算表达式的结果封装到一个对象里面去了,当然,我们得先将加法操作符(以及其它操作符)重载一下:

VecTmp operator+(const vector<int>& op1, const vector<int>& op2) {
  return VecTmp(OT_ADD, op1, op2);
}
</div>

这样一来,对于 v1 + v2,我们就得到了一个非常轻量的 VecTmp 对象,而该对象可以很轻松地转化 v1 + v2 的结果(遍历一遍 VecTmp 中的操作数)。但上面的做法还不能处理 v1 + v2 * v3 这样的套嵌的复杂表达式:v2 * v3 得到一个 VecTmp,那 v1 + VecTmp 怎么搞呢?

同理,我们还是得把 v1 + VecTmp 放到一个轻量的对象里,因此最好我们的 VecTmp 中保存的操作数也能是 VecTmp 类型的,有点递归的味道。。。用模板就可以了,于是得到如下代码:

#include <vector>
#include <iostream>

using namespace std;

enum OpType {
  OT_ADD,
  OT_SUB,
  OT_MUL,
  OT_DIV,
};

template<class T1, class T2>
class VecSum {
    OpType type_;
  const T1& op1_;
  const T2& op2_;
 public:
  VecSum(int type, const T1& op1, const T2& op2): type_(type), op1_(op1), op2_(op2) {}
 
    int operator[](const int i) const {
      switch(type_) {
        case OT_ADD: return op1_[i] + op2_[i];
        case OT_SUB: return op1_[i] - op2_[i];
        case OT_MUL: return op1_[i] * op2_[i];
        case OT_DIV: return op1_[i] / op2_[i];
        default: throw "bad type";
      }
    }
};

template<class T1, class T2>
VecSum<T1, T2> operator+(const T1& t1, const T2& t2) {
  return VecSum<T1, T2>(OT_ADD, t1, t2);
}

template<class T1, class T2>
VecSum<T1, T2> operator*(const T1& t1, const T2& t2) {
  return VecSum<T1, T2>(OT_MUL, t1, t2);
}

int main() {
  std::vector<int> v1{1, 2, 3}, v2{4, 5, 6}, v3{7, 8, 9};
  auto r = v1 + v2 * v3;
  for (auto i = 0; i < r.size(); ++i) {
    std::cout << r[i] << " ";
  }
}

</div>

上面的代码漂亮地解决了前面提到的效率问题,扩展性也很好而且对 vector 来说还是非侵入性的,虽然实现上乍看起来可能不是很直观,除此也还有些小问题可以更完善些:

操作符重载那里很可能会影响别的类型,因此最好限制一下,只针对 vector 和 VecTmp 进行重载,这里可以用 SFINAE 来处理。

VecTmp 的 operator[] 函数中的 switch 可以优化掉,VecTmp 模板只需增加一个参数,然后对各种运算类型进行偏特化就可以了。

VecTmp 对保存的操作数是有要求的,只能是 vector 或者是 VecTmp<>,这里也应该用 SFINAE 强化一下限制,使得用错时出错信息好看些。

现在我们来重头再看看这一小段奇怪的代码,显然关键在于 VecTmp 这个类,我们可以发现,它的接口其实很简单直白,但它的类型却可以是那么地复杂,比如说对于 v1 + v2 * v3 这个表达式,它的结果的类型是这样的: VecTmp<vector<int>, VecTmp<vector<int>, vector<int>>>,如果表达式再复杂些,它的类型也就更复杂了,如果你看仔细点,是不是还发现这东西和哪里很像?像一棵树,一棵类型的树。

这棵树看起来是不是还很眼熟,每个叶子结点都是 vector,而每个内部结点则是由 VecTmp 实例化的:这是一棵类型的树,在编译时就确定了。这种通过表达式在编译时得到的复杂类型有一个学名叫: Expression template。在 c++ 中每一个表达式必产生一个结果,而结果必然有类型,类型是编译时的东西,结果却是运行时的。像这种运算表达式,它的最终类型是由其中每一步运算所产生的结果所对应的类型组合起来所决定的,类型确定的过程其实和表达式的识别是一致的。

VecTmp 对象在逻辑上其实也是一棵树,它的成员变量 op1_, op2_ 则分别是左右儿子结点,树的内部结点代表一个运算,叶子结点则为操作数,一遍中序遍历下来,得到的就是整个表达式的值。

神奇的 boost::proto

expression template 是个好东西(就正如 expression SFINAE 一样),它能帮助你在编译时建立非常复杂好玩的类型系统(从而实现很多高级玩意,主要是函数式)。但显然如果什么东西都需要自己从头开始写,这个技术用起来还是很麻烦痛苦的,好在模板元编程实在是个太好玩的东西,已经有很多人做了很多先驱性的工作,看看 boost proto 吧,在 c++ 的世界里再打开一扇通往奇怪世界的大门

</div>
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++中vector和map的删除方法(推荐)
  • C++中vector容器的用法
  • C++中vector容器使用详细说明
  • C++从文本文件读取数据到vector中的方法
  • C++实现 vector 的四则运算
  • C++中vector容器的常用操作方法实例总结
  • 深入理解C++中的vector类的用法及特性
  • C++ Vector用法详解
  • C++模板类的用法
  • C++中vector的用法实例解析

相关文章

  • 2017-05-28浅析C/C++中sort函数的用法
  • 2017-05-28C++ const引用、临时变量 引用参数详解
  • 2017-05-28如何查看进程实际的内存占用情况详解
  • 2017-05-28用C++类实现单向链表的增删查和反转操作方法
  • 2017-05-28纯C语言:递归最大数源码分享
  • 2017-05-28详解C++中二进制求补运算符与下标运算符的用法
  • 2017-05-28C/C++的浮点数在内存中的存储方式分析及实例
  • 2017-05-28解析VC中预编译头文件的深入分析
  • 2017-05-28C语言使用openSSL库AES模块实现加密功能详解
  • 2017-05-28深入分析C++中两个大数相乘结果不正确的问题

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C语言时间处理实例分享
    • C中qsort快速排序使用实例
    • 对比分析C语言中的gcvt()和ecvt()以及fcvt()函数
    • C++随机数生成实例讲解
    • C++实现的归并排序算法详解
    • C++中字符串以及数组和指针的互相使用讲解
    • C语言结构体详解,C语言struct用法详解
    • 在C语言编程中使用变量的基础教程
    • 使用gcc在命令行中预定义宏
    • C语言 数组指针详解及示例代码

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有