参考链接:https://blog.youkuaiyun.com/zgl_dm/article/details/1767201
参考书籍:Primer C++ 第十四章
重载运算符与类型转换(一)
1.基本概念
C++中预定义的运算符的操作对象只能是基本数据类型。对于许多用户自定义类型(例如类),也需要类似的运算操作。
这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载
的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。
重载运算符的定义:由关键字operator和其后要定义的运算符号共同组成。函数运算符也包括返回类型、参数列表和
函数体。其一般格式为:
<返回类型说明符> operator <运算符符号>(参数列表)
{
<函数体>
}
注:1) 除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中
的所有运算符都可以重载。
2)重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
3)运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则(同一作用域内的几个函
数名字相容但是形参列表不同,则为函数重载)。
直接调用一个重载的运算符函数
//一个非成员运算符函数的等价调用
data1 + data2; //普通的表达式
operator + (data1,data2); //等价函数调用
//显式调用成员运算符函数
data1 += data2; //基于调用的表达式
data1.operator+= (data2); //对成员运算符函数的等价调用
选择作为成员或者非成员
当我们定义重载运算符时,我们必须首先决定将其声明为类的成员函数还是声明为一个普通的非成员函数。其判断准则为:
1)赋值(=)、下标([])、调用(())和成员访问箭头(->)运算符必须是成员;
2)复合赋值运算符一般来说应该是成员;
3)改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符(* 返回该指针指向的对象),
4)具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,他们通常应该是普通的非 成员函数。
2.输入和输出运算符
重载输出运算符
输出运算符的第一个形参是一个非常量的ostream对象的引用(不会改变其值),第二个形参是常量的引用,其返回值一般为它的ostream形参。输入输出运算符必须是非成员函数。
例:
ostream &operator<<(ostream &os,const Sales_data &item)
{
os <
3.算术和关系运算符
通常情况下,我们把算术和关系运算符定义成非成员函数以允许对左侧或者右侧的运算对象进行转换。因为这些运算符一般
不需要改变运算对象的状态,所以形参都是常量。
例:
Sales_data
operator +(const Sales_data &lhs,const Sales_data &rhs)
{
Sales_data sum = lhs;
sum += rhs;
return sum;
}
相等运算符
例:
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);
}
设计准则:
1)如果一个类含有判断两个对象是否相等的操作,则它显然应该把函数定义成operator==,如果类定义了operator==,则该
运算符应该能判断一组给定的对象中是否含有重复的数据;
2)通常情况下,相等运算符具有传递性;
3)如果类定义了operator==,则这个类也应该定义operator !=;
4)相等运算符和不相等运算符中的一个应该把工作委托给另外一个,这意味着其中一个运算符应该负责实际比较对象的工作,而另一个运算符只是调用那个真正工作的运算符。
**关系运算符**
例:
bool compareIsbn(const Sales_data &lhs,const Sales_data &rhs)
{
return lhs.isbn()
4.赋值运算符
赋值运算符必须定义为成员函数,通常返回一个指向其左侧运算对象的引用。
class Foo
{
public:
Foo& operator=(const Foo&);
};
class StrVec
{
public:
StrVec &operator=(std::initializer_list);
};
strVec &StrVec::operator=(initializer_list i1)
{
//alloc_n_copy 分配内存空间并从给定范围内拷贝元素
auto data = alloc_n_copy(i1.begin(),i1.end());
free(); //销毁对象中的元素并释放内存空间
elements = data.frist; //更新数据成员使其指向新空间
first_free = cap =data.second;
return *this;
}
复合赋值运算符
复合赋值运算符不非得是类的成员,不过我们还倾向于把包括复合赋值在内的所有赋值运算都定义在类的内部。
为了与内置类型的复合赋值保持一致,类中的复合赋值运算符也要返回其左侧运算对象的引用。
Sales_data& Sales_data::operator += (const Sales_data &rhs)
{
bookNo = rhs.bookNo;
units_sold = rhs.units_sold;
revenue = rhs.revenue;
return *this; //返回一个此对象的引用
}