C++ Primer:第14章 重载运算与类型转换



第14章 重载运算与类型转换

要求 运算符
可以重载 +-*/%^\|~=++--
+=-=*=/=%=^=\|=&=
!<><=>===!=<<>><<=>>=
->->*[]()newnew[]deletedelete[]
不建议重载 ,&&&\|\|
不能重载 ::.*.?:
必须是成员函数 =[]()->
建议是成员函数 +=-=*=/=%=^=\|=&=<<=>>=
++--*
建议是非成员函数 +-*/%^\|~
!<><=>===!=
必须是非成员函数 <<>>
  1. 若已定义perator==,则最好也定义operator!=;若已定义operator<,则最好也定义其它关系运算符;若类中含有算术运算符或位运算符,则最好提供对应的复合赋值运算符。
  2. 除重载的函数调用运算符operator()外,其它重载运算符不能含有默认实参。

14.1 基本概念

  1. 运算符函数必须是类的成员或者至少含有一个类类型的参数。若是成员函数,this绑定到左侧运算对象,其参数数量比运算对象数量少一个。
  2. 重载运算符的优先级、结合律与对应的内置运算符保持一致,但无法保留求值顺序和短路求值属性。重载运算符可以是成员函数或非成员函数。
  3. 只有当操作的含义对于用户来说清晰明了时才重载运算符。若用户可能对运算符有几种不同的理解,重载运算符会产生二义性。
// 非成员函数调用方法
data1 + data2;
operator+(data1, data2);
// 成员函数调用方法
data1 += data2;
data1.operator+=(data2);

14.2 输入和输出运算符

  1. 输入输出运算符必须是非成员函数,一般被声明为友元。输出运算符应尽量减少格式化操作,如不应该打印换行符。 输入运算符必须处理输入可能失败的情况,而输出运算符不需要。
  2. 当流中含有错误类型的数据时读取操作可能失败。当读取操作到达文件末尾或遇到输入流的其它错误时也会失败。当读取操作发生错误时,输入运算符应该负责将其从错误中恢复,如将对象置为合法的状态。
istream &operator>>(istream &is, const Sales_data &item)
ostream &operator<<(ostream &os, const Sales_data &item)

14.3 算术和关系运算符

  1. 如果类中同时定义算术运算符和相关的复合赋值运算符,则应使用复合赋值运算符来实现算术运算符,因为复合赋值运算符只会修改左侧运算对象,而算术运算符会创建一个新对象。
  2. 若存在唯一一种逻辑可靠的<定义,则应考虑为该类定义<运算符。若类中同时还包含==,则当且仅当<的定义和==产生结果一致时(若两个对象不相等,则一个对象应该小于另一个对象)才能定义<运算符。
Sales_data &operator+=(const Sales_data &);
Sales_data &operator+(const Sales_data  &lhs, const Sales_data &rhs)
bool operator==(const Sales_data  &lhs, const Sales_data &rhs)
bool operator<(const StrBlob &lhs, const StrBlob &rhs)

14.4 赋值运算符

StrVec &operator=(std::initializer_list<std::string>);

14.5 下标运算符

  1. 若类包含下标运算符,则通常定义两个版本:一个返回普通引用,另一个是类的常量成员并且返回常量引用。
string &operator[](size_t n);
const string &operator[](size_t n) const;

14.6 递增和递减运算符

  1. 定义递增递减运算符应同时定义前置版本和后置版本。后置版本比前置版本多接受一个int类型的形参。
ConstStrBlobPtr &operator++();
ConstStrBlobPtr &operator--();
ConstStrBlobPtr operator++(int);
ConstStrBlobPtr operator--(int);

14.7 成员访问运算符

  1. 箭头运算符只能用于获取成员,其运算对象必须是指向类对象的指针或重载->的类的对象。
std::string &operator*() const;
std::string *operator->() const;

14.8 函数调用运算符

  1. 调用运算符可以定义多个不同版本,但要求参数数量或类型有所区别。函数对象即定义调用运算符的类的对象。函数对象其行为类似于函数,常用于泛型算法的实参。
  2. lambda被编译器翻译成一个含有重载调用运算符的未命名类的未命名对象。未命名类的调用运算符默认是const,若lambda被声明为mutable,则调用运算符是非const
  3. 若是值捕获,lambda产生的类必须为捕获变量创建数据成员和构造函数。若是引用捕获,则编译器直接使用该引用,而不会创建数据成员。
  4. lambda产生的类不含默认构造函数、赋值运算符及默认析构函数;其是否含有默认拷贝构造函数和移动构造函数由捕获的数据成员类型决定。
std::string operator()(void) const
std::string operator()() const
bool operator()(const std::string &s)
  1. 标准库定义的函数对象是模板,使用时需指定类型。函数对象同样适用于指针,虽然无法直接比较两个无关的指针,但可使用标准库函数对象比较两个无关指针。
标准库定义的函数对象 说明(#include <functional>
算术 plus<Type>``````minus<Type>``````multiplies<Type>``````divides<Type>
modulus<Type>
negate<Type>
关系 equal_to<Type>``````not_equal_to<Type>
greater<Type>``````greater_equal<Type>
less<Type>``````less_equal<Type>
逻辑 logical_and<Type>``````logical_or<Type>``````logical_not<Type>
  1. C++可调用对象包括函数、函数指针、lambda表达式、bind创建的对象、重载调用运算符的类。不同可调用对象可能具有相同的调用形式。
  2. function是模板。不能直接将重载函数名字存入function对象,可通过存储函数指针或lambda来消除二义性。
标准库function类型 示例(#include <functional>
function<T> f;
function<T> f(nullptr);
function<T> f(obj);
定义一个存储可调用对象的function
f; f含有可调用对象则返回true,否则返回false
f(args); 调用f中的对象,参数为args
result_type 可调用对象的返回类型
argument_type
first_argument_type
second_argument_type
可调用对象的参数类型

14.9 重载、类型转换与运算符

  1. 转换构造函数将实参类型转换成类类型,类型转换运算符将类类型转换成其它类型。转换构造函数和类型转换运算符共同定义类类型转换(用户定义的类型转换)。
  2. 类型转换函数必须是类的成员函数,不能声明返回类型,形参列表必须为空,通常是const。除void外,只要某一类型可以作为函数返回类型,类型转换运算符便可将类类型转换成该类型。
  3. 显式的类型转换与显式构造函数一样可阻止编译器作隐式类型转换,但表达式被作为判断条件时,编译器会隐式执行显式的类型转换。转换目标是bool的类型转换常用作判断条件,故operator bool一般定义成explicit
  4. 编译器只允许进行一次类类型转换和一次算术转换,其顺序不限
  5. 若两个类提供相同的类型转换或类定义多个转换规则,则可能产生多重转换路径。故通常不要为类定义相同的类型转换,也不要在类中定义两个及两个以上转换源或转换目标是算术类型的转换。
  6. 调用重载函数时,若多个用户定义的类型转换都提供可行匹配,即使其中存在精确匹配,编译器也认为调用存在二义性。
  7. 在表达式中,可以通过函数匹配区分内置运算符和重载运算符,不能通过调用形式区分成员函数和非成员函数。若类既有转换目标是算术类型的类型转换,又有重载运算符,则会产生内置运算符与重载运算符的二义性问题。
// 类型转换运算符一般形式
operator type() const;
// 显式的类型转换运算符
explicit operator int(){};

猜你喜欢

转载自blog.csdn.net/qq_34801642/article/details/107635045