文章目录
- 1.概述
- 2.输入输出运算符
- 3.算术关系运算符
- 4.赋值运算符
- 5.函数对象
- 6. 可调用对象与function
1.概述
自定义类可以重载运算符,大多数的运算符都是可以重载的,下表给出了那些运算符可以重载那些不可以重载。
对于运算符的重载,我们需要明白运算符是如何调用的。
//非成员函数的运算符调用
d1+d2;//表达式方式调用
operator+(d1,d2);//等价的调用
//成员函数的运算符调用方式
d1+=d2;//运算符调用
d1.operator+=(d2);//等价的调用
成员函数和非成员函数的选择
在《c++primer》中有以下准则:
对于重载函数是否应该是成员函数,在具体的操作符重载时进行说明。
2.输入输出运算符
IO操作中分别用"<<、>>"两个符号表示输出和输入,重载时返回IO对象的引用。
重载输出运算符<<
- 第一个形参为非常量的ostream对象的引用。
- 第二个参数一般来说是常量的引用
- 返回值为ostream&,一般为第一个形参。
重载输入运算符>>
- 第一个运算符为将要读取的流的引用。
- 第二个形参为将要读入数据的对象的非常量的引用。
- 错误检查,输入的数据类型或者格式不符合时将会产生错误。
- 返回值类型一般为输入的第一个形参。
输入输出运算符必须是非成员函数,否则在调用时左侧必须是一个类的对象,为了能够访问私有成员,类需要将重载函数声明为友元函数。
案例:
#include <iostream>
#include<string>
#include<vector>
using namespace std;
class Person
{
public:
//声明为友元函数
friend ostream& operator<<(ostream& os, const Person& p);
friend istream& operator>>(istream& is, Person& p);
Person( string name, int age, string sex)
:Name(name),Age(age),Sex(sex){}
Person()
:Name(" "), Age(0), Sex("") {}
private:
string Name;
int Age;
string Sex;
};
ostream& operator<<(ostream& os, const Person& p)
{
os << p.Name << " " << p.Age << "岁" << " " << "性别: " << p.Sex;
return os;
}
istream& operator>>(istream &is,Person &p)
{
is >> p.Name >> p.Age >> p.Sex;
if (!is)
{
p = Person();//如果输入错误应该将对象复原
}
return is;
}
int main()
{
Person p;
cin >> p;
cout << p;
}
对于输入运算符中的错误检查我们可以更加完善的表示错误,具体如何操作参考文章:IO操作
3.算术关系运算符
通常情况下我们把算术和关系运算符定义成非成员函数以允许对左侧或者右侧的运算对象进行转换,关系运算符一般不需要改变运算对象的状态,所以形参都是常量的引用。
案例:
bool operator ==(const Person& p1, const Person& p2)
{
if (p1.Age == p2.Age && p1.Name == p2.Name && p1.Sex == p2.Sex)
return false;
else
return true;
}
bool operator<(const Person &p1,const Person &p2)
{
if (p1.Age < p2.Age)
return false;
else
return true;
}
bool operator>(const Person& p1, const Person& p2)
{
if (p1.Age > p2.Age)
return false;
return true;
}
4.赋值运算符
赋值运算符的重载包含两种版本,一个是拷贝赋值运算符,另一个是移动赋值运算符。
拷贝赋值运算符参考:拷贝赋值运算符
移动赋值运算符:移动赋值
5.自增自减运算符(++、- -)
自增自减操作一般定义为类的成员函数,因为递增递减操作改变了类的内部状态,一般需要定义成为成员函数。递增递减操作需要考虑两种情形,一种是前置的、另一种是后置的。
前置版本
Person& operator++()
{
++Age;
return *this;
}
后置版本的递增递减运算符为了和前置版本的进行区别,在参数列表中加入int关键字加以区别。
Person& operator++(int)
{
Person temp = *this;
++*this;
return temp;
}
5.函数对象
所谓函数对象就是函数重载了函数调用运算符"()",当某个类重载了函数调用运算符我们可以象调用函数一样的方式调用该类的对象。调用运算符必须是成员函数,一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或者类型上有所区别。
class test
{
public:
test(string str=""):s(str) {}
void operator()(string s1)
{
cout << s + s1 << endl;
}
private:
string s;
};
test t("hello");
t(" world");
上述对象的调用将会打印hello world
标准库定义的函数对象:
上述表中的函数调用对象定义在头文件functional中,比如将greater用于排序sort函数等。
6. 可调用对象与function
C++语言中有很多种可调用对象:函数、函数针针、lambda表达式、bind对象等。在在不同的可调用函数中可以有相同的调用形式,一般调用形式为:type (typeA,typeB)
,如add函数和sub函数都有相同调用形式int(int,int)。
function是一种函数模板,用function声明的调用形式可以接收所有的据相同的调用形式的可调用对象;
int add(int v1, int v2)
{
return v1 + v2;
}
class Add
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
function<int(int, int)> f1 = add;//函数指针
function<int(int, int)> f2 = Add();//函数对象类
function<int(int, int)> f3 = [](int v1, int v2)//lambda表示式
{return v1 + v2; };
cout << f1(5, 5) << endl;
cout << f2(5, 5) << endl;
cout << f3(5, 5) << endl;