14、操作重载和类型转换
14.2 输入和输出运算符
14.2.1 重载输出运算符<<
为类自定义IO运算符,必须将其定义为非成员函数 ,同时IO运算符通常需要读写类的非公有数据成员,所以IO运算符一般被声明为友元。
14.2.2 重载输入运算符>>
注:输入运算符必须处理输入可能失败的情况,而输出运算符不需要
14.3 算术和关系运算符
14.3.1 相等运算符
14.3.2 关系运算符
14.4 赋值运算符
注:不论形参的类型是什么,赋值运算符都必须定义为成员函数。
14.5 下标运算符
表示容器的类通常可以通过元素在容器中的位置访问元素,这些类一般会定义下标运算符operator[]。
注:下标运算符必须是成员函数,以所访问元素的引用作为返回值。
14.6 递增和递减运算符
C++语言不要求递增和递减运算符必须是类的成员,但是因为它们改变的正好是所操作对象的状态,所以建议将其设定为成员函数。为类定义定义前置版本和后置版本递增和递减运算符。
定义前置递增/递减运算符
注:为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。
区分前置和后置运算符
为了解决同时定义前置和后置运算符这个问题,后置版本提供一个额外(不被使用)int类型的形参。
后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用。
14.7 成员访问运算符
箭头运算符(->)不执行任何自己的操作,而是调用解引用运算符(*)并返回解引用结果元素的地址。
注:箭头运算符必须是类的成员。解引用运算符通常也是类的成员,尽管并非如此。
对箭头运算符返回值的限定
重载箭头运算符时,可以改变的是箭头从哪个对象当中获取成员,而箭头获取成员这一事实永远不变。
注:重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象。
14.8 函数调用运算符
注:函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所不同。
若类定义了调用运算符,则该类的对象称作函数对象。
含有状态的函数对象类
14.8.1 lambda是函数对象
编写一个lambda后,编译器将该表达式翻译成一个未命名类的未命名对象。lambda表达式产生的类中含有一个重载的函数调用运算符。
表示lambda及相应捕获行为的类
当一个lambda表达式通过引用捕获变量时,由程序负责确保lambda执行时引用所引的对象确实存在。编译器无须在lambda产生的类中将其存储为数据成员。
当通过值捕获的变量被拷贝到lambda中,编译器需要在lambda产生的类中将其存储为数据成员,并使用捕获的变量值来初始化数据成员。
14.8.2 标准库定义的函数对象
定义在functional头文件中
在算法中使用标准库函数对象
14.8.3 可调用对象与function
定义一个函数表用于存储指向这些可调用对象的"指针"。
标准库function类型
function<int(int,int)>可以存储不同类型的可调用对象,如函数指针、函数对象、lambda对象等。
重载的函数与function函数
不能直接将重载函数的名字存入function类型的对象中。
14.9 重载、类型转换与运算符
转换构造函数和类型转换运算符共同定义了类类型转换,这样的转换有时被称作用户定义的类型转换。
14.9.1 类型转换运算符
类型转换运算符是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。形式如: operator type() const;
注:类型转换函数必须是类的成员函数;它不能声明返回类型,形参列表也必须为空。通常是const
类型转换运算符可以面向任意类型(除了void)进行定义,只要该类型能作为函数的返回类型。
定义含有类型转换运算符的类
涉及隐式类型转换
类型转换运算符可能产生意外结果
int i = 42;
cin << i; //若向bool的类型转换不是显式的,则该代码在编译器看来是合法的
因为istream本身没有定义<<,所以本来这段代码产生错误。然而,istream的bool类型转换运算符将cin转换成bool,而这个bool值接着会被提升为int并作用内置的左移运算符的左侧运算对象。
显式的类型转换运算符
为了防止上面的异常情况发生,C++11新标准引入了显式的类型转换运算符:
class SmallInt{
public:
//编译器不会自动执行这一类型转换
explicit operator int() const {return val;}
};
编译器不会将一个显式的类型转换符用于隐式类型转换:
SmallInt si=3;//正确:SmallInt的构造函数不是显式的
si+3; //此处需要隐式的类型转换,但类的运算符是显式的
static_cast<int>(si)+3;//正确:显式地请求类型转换(强制类型转换)
该规定存在一个例外,即如表达式被用作条件,则编译器会将显式的类型转换自动应用于它。
转换为bool
在C++11新标准下,IO标准库定义了一个向bool的显式类型转换实现同样的目的。无论我们什么时候在条件中使用流对象,都会使用IO类型定义的operator bool。例如:
while (std::cin >> value)
while语句的条件执行输入运算符,它负责将数据读入到value并返回cin。为了对条件求值,cin被istream operator bool类型转换函数隐式地执行了转换。如果cin的条件状态是good,则该函数返回真;否则该函数返回为假。