通常情况下,我们把算术和关系运算符定义成非成员函数以允许对左侧或右侧的运算对象进行转换。因为这些运算符一般不需要改变运算对象的状态,所以形参都是常量的引用。
算术运算符通常会计算它的两个对象并得到一个新值,这个值有别于任意一个运算对象,常常位于一个局部变量之内,操作完成后返回该局部变量的副本作为其结果。如果类定义了算术运算符,则它一般也会定义一个对应的复合赋值运算符。此时,最有效的方式是使用复合赋值来定义算术运算符:
//假设两个对象指向同一本书
Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum += rhs;
return sum;
}
我们把lhs拷贝给局部变量sum,然后使用Sales_data的复合赋值运算符将rhs的值加到sum中,最后函数返回sum的副本。
如果类同时定义了算术运算符和相关的复合赋值运算符,则通常情况下应该使用复合赋值来实现算术运算符。
你认为Sales_data类还应该支持哪些其他的算术运算符,给出它们的定义
class Sales_data {
friend Sales_data operator-(const Sales_data& lhs, const Sales_data& rhs);
public:
Sales_data& operator-=(const Sales_data &rhs)
};
Sales_data operator-(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sub = lhs;
sub -= rhs;
return sub;
}
Sales_data& Sales_data::operator-=(const Sales_data& rhs) {
units_sold -= rhs.units_sold;
revenue -= rhs.revenue;
return *this;
}
为什么调用operator+=来定义operator+比其他方法更有效
显然,从头实现operator+的方式与借助operator+=实现的方式相比,在性能上没有优势,而可读性上后者显然更好。因此,在此例中代码复用是最好的方式。
相等运算符
通过定义相等运算符来检验两个对象是否相等。会比较对象中的每一个数据成员,只有当所有对应的成员都相等时才认为两个对象都相等。
bool operator==(const Sales_data& lhs, const Sales_data& rhs) {
return lhs.isbn() == rhs.isbn() && lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue;
}
bool operator!=(const Sales_data& lhs, const Sales_data& rhs) {
return !(lhs == rhs);
}
相等运算符和不相等运算符中的一个应该把工作委托给另一个,这意味着其中一个运算符应该负责实际比较对象的工作,而另一个运算符则只调用那个真正工作的运算符。
为StrBlob类、StrBlobPtr类、StrVec类、String分别定义相等运算符和不相等运算符
class StrBlob {
friend bool operator==(const StrBlob& lhs, const StrBlob& rhs);
friend bool oprator!=(const StrBlob& lhs, const StrBlob& rhs);
};
bool operator==(const StrBlob& lhs, const StrBlob& rhs) {
return lhs.data == rhs.data;
}
bool operator!=(const StrBlob& lhs, const StrBlob& rhs) {
return !(lhs == rhs);
}
class StrBlobPtr {
friend bool operator==(const StrBlobPtr& lhs, const StrBlobPtr& rhs);
friend bool oprator != (const StrBlobPtr& lhs, const StrBlobPtr& rhs);
};
bool operator==(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
if (l == r) //两个指针都为空,或指向相同的vector且curr指向相同元素时,相等,否则不相等
return (!r || lhs.curr == rhs.curr);
else
return false;
}
bool operator!=(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
return !(lhs == rhs);
}
class StrVec {
friend bool operator==(const StrVec& lhs, const StrVec& rhs);
friend bool operator!=(const StrVec& lhs, const StrVec& rhs);
};
bool operator==(const StrVec& lhs, const StrVec& rhs) {
if (lhs.size() != rhs.size()) {
return false;
}
for (auto itr1 = lhs.begin(), itr2 = rhs.begin(); itr1 != lhs.end() && itr2 != rhs.end(); itr1++, itr2++) {
if (*itr1 != *itr2) {
return false;
}
}
return true;
}
bool operator!=(const StrVec& lhs, const StrVec& rhs) {
return !(lhs == rhs);
}
class String {
friend bool operator==(const String& lhs, const String& rhs);
friend bool operator!=(const String& lhs, const String& rhs);
private:
const char* str;
};
bool operator==(const String& lhs, const String& rhs) {
return strcmp(lhs.str, rhs.str);
}
bool operator!=(const String& lhs, const String& rhs) {
return !(lhs == rhs);
}
关系运算符
定义了相等运算符也常常包含关系运算符。特别是,因为关联容器和一些算法要用到小于运算符,所以定义operator<会比较有用。
通常情况下关系运算符应该
1、定义顺序关系,令其与关联容器中对关键字的要求一致。
2、如果类同时也含有==运算符的话,则定义一种关系令其与等于保持一致。特别是,如果两个对象是!=的,那么一个对象应该小于另外一个。
如果存在唯一一种逻辑可靠的<定义,则应该考虑为这个类定义<运算符。如果类同时还包含==,则当且仅当<的定义和等于产生的结果一致时才定义<运算符。
不会code的菜鸟 发布了79 篇原创文章 · 获赞 4 · 访问量 2270 私信 关注