多产品的网站怎么做seo,网站设计的基本过程,网站上线前的准备,做新闻封面的网站operators
C提供了强大且自由的操作符重载能力#xff0c;可以把大多数操作符重新定义为函数#xff0c;使操作更加简单直观。这方面很好的例子就是标准库中的string和 complex#xff0c;可以像操作内置类型int、double那样对它们进行算术运算和比较运算#xff0c;非常方…operators
C提供了强大且自由的操作符重载能力可以把大多数操作符重新定义为函数使操作更加简单直观。这方面很好的例子就是标准库中的string和 complex可以像操作内置类型int、double那样对它们进行算术运算和比较运算非常方便。
但实现重载操作符却比使用它要麻烦许多因为很多运算具有对称性如果定义了operator那么很自然需要operator-如果有小于比较那么也应该有小于等于、大于、大于等于比较。完全实现这些操作符的重载工作是单调乏味的而且增加的代码量也增加了出错的可能性还必须保证这些操作符都实现了正确的语义。
实际上很多操作符可以从其他的操作符自动推导出来例如a !b可以是!(ab)ab可以是!(ab)。因此原则上只需要定义少量的基本操作符其他的操作符就可以用逻辑组合实现。
在c标准的std::rel_ops名字空间里提供了四个模板比较操作符 !、、、只需要为类定义了和操作符那么这四个操作符就可以自动实现。
#include utility
class demo_class //一个定义operator的类
{
public:demo_class(int n) :x(n) {}int x;friend bool operator(const demo_class l, const demo_class r){return l.x r.x;}
};void case1()
{demo_class a(10), b(20);using namespace std::rel_ops; //打开std::rel_ops名字空间assert(a b); //自定义的操作符assert(b a); //等操作符被自动实现
}但std::rel_ops的解决方案过于简单还很不够。除了比较操作符还有很多其他的操作符重载标准库没有给出解决方案而且使用这些操作符需要用using 语句导入std::rel_ops名字空间不方便也会带来潜在的冲突风险。
boost.operators库因此应运而生。它采用类似std::rel_ops 的实现手法允许用户在自己的类里仅定义少量的操作符(如)就可方便地自动生成其他操作符重载而且保证正确的语义实现。
operators位于名字空间boost,为了使用operators组件需要包含头文件boost/operators.hpp即
#include boost/operators.hpp
using namespace boost;基本运算概念
由于C可重载的操作符非常多因此 operators库是由多个类组成的分别用来实现不同的运算概念比如 less_than_comparable定义了系列操作符left_shiftable定义了系列操作符。
operators中的概念很多包括了C中的大部分操作符重载在这里我们先介绍一些最常用的算术操作符
equality_comparable : 要求提供, 可自动实现!, 相等语义;
less_than_comparable : 要求提供, 可自动实现、、:
addable : 要求提供, 可自动实现;
subtractable : 要求提供-, 可自动实现-;
incrementable : 要求提供前置, 可自动实现后置;
decrementable : 要求提供前置--, 可自动实现后置--;
equivalent : 要求提供, 可自动实现-, 等价语义。这些概念在库中以同名类的形式提供用户需要以继承的方式来使用它们。继承的修饰符并不重要private、public都可以因为 operators库里的类都是空类没有成员变量和成员函数仅定义了数个友元操作符函数。
例如less_than_comparable的形式是
template class T
struct less_than_comparable {friend bool operator (const T x, const T y);friend bool operator (const T x, const T y);friend bool operator (const T x, const T y);
};如果要同时实现多个运算概念则可以使用多重继承技术把自定义类作为多个概念的子类但多重继承在使用时存在很多问题稍后将看到operators库使用了特别的技巧来解决这个问题。
算术操作符的用法
class point :
{int x, y, z;
public:explicit point(int a 0, int b 0, int c 0) :x(a), y(b), z(c) {}void print()const{cout x , y , z endl;}
}; 我们先来实现less_than_comparable它要求point类提供操作符并由它继承。假定point的小于关系是由三个坐标值的平方和决定的下面的代码示范了less_than_comparable的用法只需要为point增加父类并定义less_than_comparable概念所要求的operator
class point : boost::less_than_comparablepoint //小于关系, 私有继承
{int x, y, z;
public:explicit point(int a 0, int b 0, int c 0) :x(a), y(b), z(c) {}void print()const{cout x , y , z endl;}friend bool operator(const point l, const point r){return (l.x * l.x l.y * l.y l.z * l.z r.x* r.x r.y * r.y r.z * r.z);}
... //其他成员函数
};less_than_comparable作为基类的用法可能稍微有点奇怪它把子类point作为了父类的模板参数less_than_comparablepoint看起来好像是个“循环继承”。实际上point类作为less_than_comparable的模板类型参数只是用来实现内部的比较操作符用做操作符函数的类型没有任何继承关系。less_than_comparable生成的代码可以理解成这样
//templateT point
struct less_than_comparable
{friend bool operator(const point x, const point y){ return !(x y); }
}明白了less_than_comparable 的继承用法剩下的就很简单了point类定义了一个友元operator操作符然后其余的、、就由less_than_comparable自动生成。几乎不费什么力气在没有污染名字空间的情况下我们就获得了四个操作符的能力
int main()
{point p0, p1(1, 2, 3), p2(3, 0, 5), p3(3, 2, 1);assert(p0 p1 p1 p2);assert(p2 p0);assert(p1 p3);assert(!(p1 p3) !(p1 p3));
}同样我们可以定义相等关系使用equality_comparable规则是point的三个坐标值完全相等需要自行实现operator
class point : boost::less_than_comparablepoint, //使用多重继承boost::equality_comparablepoint //新增相等关系
{
public:friend bool operator(const point l, const point r){ /*同前*/ }friend bool operator(const point l, const point r){ return r.x l.x r.y l.y r.z l.z; }
};然后我们就自动获得了operator!定义
point p0, p1(1,2,3), p2(p1), p3(3,2,1);
assert(p1 p2);
assert(p1 ! p3);在使用operators库时要注意一点模板类型参数必须是子类自身特别是当子类本身也是个模板类的时候不要错写成子类的模板参数或者子类不带模板参数的名称否则会造成编译错误。假如我们改写point类为一个模板类
templatetypename T class point {...};那么如下的形式都是错误的
templatetypename T class point: boost::less_than_comparableT
templatetypename T class point: boost::less_than_comparablepoint正确的写法应该是
templatetypename T class point: boost::less_than_comparablepointT因为只有pointT才是模板类point的全名。
基类链
多重继承一直是C中引发争论的话题喜欢它的人和讨厌它的人几乎同样多。总的来说多重继承是一种强大的面向对象技术但使用不当也很容易引发诸多问题比如难以优化和经典的“钻石型”继承。
operators库使用泛型编程的“基类链”技术解决了多重继承的问题这种技术通过模板把多继承转换为链式的单继承。
前面当讨论到 less_than_comparablepoint这种用法时我们说它不是继承然而现在我们将看到它居然真的可以实现继承的功能这从一个方面展示了泛型编程的强大威力。
operators库的操作符模板类除了接受子类作为比较类型外还可以接受另外一个类作为它的父类由此可以无限串联链接在一起但要受编译器的模板编译能力限制)像这样
demo: xdemo, ydemo, zdemo, ... 使用基类链技术point类的基类部分可以是这样
boost::less_than_comparablepoint, //注意这里
boost::equality_comparablepoint //是一个有很大模板参数列表的类对比一下多重继承的写法
boost::less_than_comparablepoint, //注意这里
boost::equality_comparablepoint //有两个类代码非常相似区别仅仅在于模板参数列表结束符号()的位置如果不仔细看可能根本察觉不出差距。但正是这个小小的差距使基类链通过模板组成了一连串的单继承链表而不是多个父类的多重继承。
例如如果为point类再增加加法和减法定义则继承列表就是
class point:less_than_comparablepoint, //小于操作equality_comparablepoint, //相等操作addablepoint, //相加操作subtractablepoint //减法操作
{...};基类链技术会导致代码出现一个有趣的形式在派生类的基类声明末尾处出现一长串的(模板声明的结束符)在编写代码时需要小心谨慎以保证尖括号的匹配使用良好的代码缩进和换行可以减少错误的发生。