目录
如何调用 非成员函数 和 成员函数的 重载运算符 (491P)
重载运算符的 返回类型 应该与 内置版本的返回类型兼容 (492P)
重载 赋值和 复合赋值运算符 应该返回左侧运算对象的一个引用 (492P)
如果一个类重载了算术运算符或者 位运算符,则最好也提供它们对应的复合赋值运算符。 (492P)
哪些运算符应该重载为成员函数、哪些重载为非成员函数 (492P)
如果类定义了算术运算符,则它一般也会定义一个相应的复合赋值运算符。此时,该类同时定义了算术运算符和对应的复合赋值运算符,那么可以使用复合赋值来实现算术运算符。(497P)
标准库定义的函数对象 (plus类、modulus类、equal_to类、等等) (509P)
可调用对象(函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符的来) 与 标准库 function 类型 (511P)
下面这个程序是本章的示例,我总结了一下:
class Sales_data
{
// 重载的输入输出运算符一般被声明为友元, 因为它们通常需要读写类的非公有成员
friend std::ostream &operator<<(std::ostream &os, const Sales_data &item);
friend std::istream &operator>>(std::istream &is, Sales_data &item);
friend bool operator==(const Sales_data &lhs, const Sales_data &rhs);
public:
Sales_data operator+=(const Sales_data &rhs);
std::string isbn() const { return bookNo; }
private:
double avg_price() const
{
return units_sold ? revenue / units_sold : 0;
}
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
std::ostream &operator<<(std::ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
std::istream &operator>>(std::istream &is, Sales_data &item)
{
double price; // no need to initialize; we'll read into price before we use it
is >> item.bookNo >> item.units_sold >> price;
if (is) // check that the inputs succeeded
item.revenue = item.units_sold * price;
else
item = Sales_data(); // input failed: give the object the default state
return is;
}
//算术运算符通常定义为为非成员函数
Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs; // copy data members from lhs into sum
sum += rhs; // add rhs into sum
return sum;
}
Sales_data Sales_data::operator+=(const Sales_data &rhs)
{
bookNo += rhs.bookNo;
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
bool operator==(const Sales_data &lhs, const Sales_data &rhs)
{ // 关系运算符通常定义为非成员函数, 如果一个类定义了 operator== , 意味着通常也应该有 operator!=, 反之亦然
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);
}
基本概念
- 除了重载 “ 函数调用运算符 operator() ” 之外, 其他重载运算符不能含有默认实参。
- 当一个重载的运算符是成员函数时, this绑定到左侧运算对象。成员运算符函数的(显式)形参数量比运算对象的数量少一个。
- 运算符函数必须是类的成员或至少具有一个类类型的参数 。 这一限制意味着,当应用于内置类型的操作数时,我们不能更改操作符的含义。
// 错误:不能为 int 重新定义新含义 int operator+(int, int);
- 我们只能重载现有的运算符,而不能创建新的运算符符号。
- 有四个运算符(+、-、*、&)既是一元运算符也是二元运算符, 这些运算符都能被重载, 从参数的数量我们可以推断到底定义的是哪种运算符。
- 对于一个重载运算符来说,其具有相同的优先级和结合律与对应的内置运算符保持一致。无论操作数类型如何
如何调用 非成员函数 和 成员函数的 重载运算符 (491P)
有哪些运算符就不应该被重载 (491P)
在本书的第四章介绍过,某些运算符指定了运算对象求值的顺序。因为使用重载的运算符本质上是一次函数调用, 所以这些关于运算对象求值顺序的规则无法应用到重载的运算符上。
所以需要注意: 通常我们不应该重载: 逗号、逻辑与、逻辑或、取地址运算符。
- 因为逻辑与、逻辑或 运算符的重载版本无法保留求值顺序 或 短路求值属性(因为两个运算对象总是会被求值)。
- 因为逗号、取地址运算符 ,C++语言已经定义了它们用于类类型对象时的特殊含义。
重载运算符的 返回类型 应该与 内置版本的返回类型兼容 (492P)
重载 赋值和 复合赋值运算符 应该返回左侧运算对象的一个引用 (492P)
如果一个类重载了算术运算符或者 位运算符,则最好也提供它们对应的复合赋值运算符。 (492P)
哪些运算符应该重载为成员函数、哪些重载为非成员函数 (492P)
- 如果我们想对类对象进行混合运算(比如说类对象和 整数相加),则运算符必须定义成普通的非成员函数。 每个实参都能被转换成形参类型, 但必须至少有一个运算对象是类类型。
- 当我们把运算符定义为成员函数时, 它的左侧运算对象必须是运算符所属类的一个类类型。
重载运算符和 内置运算符的区别(493P)
不同点
- 运算符函数如果是类的成员,至少具有一个类类型的参数
- 重载的运算符不再保留求值顺序等等,例如,逻辑与 和 逻辑或 不再保留 求值顺序 和 短路求值属性,两个运算对象都会被求值
相同点
- 无论运算对象的类型是什么, 对于一个重载的运算符来说,其结合律 和 优先级与对应的内置版本保持一致
- 运算对象的个数保持不变
重载输出运算符 (494P)
输出运算符不应该打印 换行符 (494P)
通常, 输出运算符应该主要负责打印对象的内容而非打印格式化操作, 尤其不应该打印换行符。如果运算符打印了换行符 ,则用户就无法在对象的同一行内接着打印一些描述性的文本了。
输入输出运算符 必须重载为 非成员函数 (494P)
- 输入和输出运算符一般重载为友元函数,因为它们通常需要读写类的非公有数据成员, 而不能是该类的成员函数。 否则, 它们的左侧运算符对象将是我们该类的一个对象了。
重载输入运算符 (495P)
istream &operator>>(istream &is, Sales_data &item) { double price; is >> item.bookNo >> item.units_sold >> price