04/23/2020
C++重载运算
- 关键字operator和其后要定义的运算符号
- 包含返回类型,参数列表以及函数体
- 参数列表重载完之后应该与原先的一样
- 一元运算符:一个参数
- 二元运算符:两个参数,例如加号左侧运算对象属于参数列表的第一个,右侧属于第二个。
- (+,-,*,&)既是一元又是二元
- 不允许有默认实参,除了函数调用运算符operator()
- 如果运算符函数是成员函数,它的第一个(左侧)运算对象绑定到隐式的this指针上,所以参数列表会少一个。
- 不能被重载的运算符:作用域运算符(:😃,成员访问运算符(.),三元条件运算符(?😃,指向成员的指针(.*)
data1 + data2;
operator+(data1,data2); //相互之间等价,本质函数调用
比较重要的重载运算符
输入输出运算符
- 输入运算符**必须**处理输入可能的情况,而输出不需要。
- 注意参数列表中输入类型的引用和返回类型的引用。
- 通常在类中,声明友元函数比较好理解,**不建议声明**成类成员函数,看例子:
- 输入输出是二元运算符,只有两个形参。
例子1: 输出重载符中的成员函数与非成员函数
class ClassType
{
public:
ostream& operator<<(ostream& os) //类成员函数,隐藏了this指针
{
os << name << age;
return os;
}
friend ostream& operator<<(ostream& os,const ClassType& item);
private:
std::string name;
int age;
};
//强调ostream对象不能被拷贝,所以用引用,返回ostream& 原因是因为可以继续连续输出别的信息;
ostream& operator<<(ostream& os,const ClassType& item) //友元函数
{
os << item.name << item.age;
return os;
}
//main function
ClassType data;
data << cout; //这边会难以理解,因为顺序是反的。
data.operator<<(cout); //和上面一个意思
cout << data; //调用友元函数的输出
cout << data << "\n";//如果返回值不是ostream&, 后续的换行符是无法输出的。
例子2: 输入重载符
istream& operator>>(istream& is, ClassType& item) //友元函数
{
is >> item.name >> item.age;
if(!is) //如果输入流失败
{
item = ClassType(); //默认构造函数被使用。
}
return is;
}
- 输出与输入有哪些不同?
- 检查流状态
- 输出重载的对象可以是const作为形参列表,而输入不可以。
istream& operator>>(istream &is, ClassType& item);//输入有可能失败,例如类型不相等,检查输入流状态
ostream& operator<<(ostream &os, const ClassType& item);//输出不需要
- 算术和关系运算符
- 加号(+),注意返回值是没有引用的
- 一组相等和不相等运算符,返回bool类型
- 关系运算符,大于小于。。。
- 赋值运算符,返回值必然是左侧对象的引用,也一定是类成员函数
- 复合赋值运算符 +=, 一定是类成员函数,返回左侧运算对象的引用
- 下标运算符
- 以中括号(“[]”)为标志作为声明,代表返回数组首个元素的地址
- 下表运算符必须是成员函数
class StrVec
{
public:
StrVec(std::size_t size):element(new string(size)){}
//版本1
std::string& operator[](std::size_t n) //下标越界问题
{
return element[n]; //返回一个string的引用
}
//版本二 类的常量成员并返回常量引用,返回值无法被改变
const std::string& operator[](std::size_t n)const;
private:
std::string* element; //指向数组首元素的指针
};
StrVec(10);
temp[2] = "zero"; //左值引用
- 递增和递减运算符
- 不要求必须是类的成员函数,但是它们改变的正好是所操作对象的状态,所以建议将其设定为成员函数
- 前置:应该返回递增或递减后对象的引用
- 后置:返回对象的原值
- 一元运算符,只有一个形参
class BOperator
{
public:
BOperator& operator++(); //前置运算符
BOperator& operator--();
BOperator operator++(); //后置运算符
BOperator operator--();
};
- 成员访问运算符
- 解引用运算符(*)和箭头运算符(->)
- 箭头运算符必须作为类的成员函数,但是解引用通常也是。
class BOperator
{
public:
std::string& opeartor*() const
{
auto p = check(curr,"dereference past end");
return (*p)[curr];
}
std::string& operator->()const
{
return &this->operator*(); //委托给解引用运算符
}
};
- 函数调用运算符1 :必须是成员函数
- 格式:operator()
- 应用:C++标准库定义的函数对象。
//函数调用运算符必须使成员函数,所以必须有类或者结构体。
//多个不同版本的调用运算符,有着不同的参数数量或类型
struct absInt //结构体
{
int operator()(int val)const{ //不要忘记小括号!operator()
return val < 0 ? -val:val;
}
double operator()(double val)const{ //可以使用数据成员
os << point <<endl;
return ++val;
}
ostream &os = cout;
float point = 3.0;
};
int i = -42;
absInt absObj;
int ui = absObj(i);//重载函数调用
标准库function类型
声明函数
#include <functional> //函数库
function<int(int,int)> f1 = add;//函数指针
function<int(int,int)> f2 = add();//函数对象类的对象
function<int(int,int)> f3 = [](int i,int j){return i*j;};
重载的函数与function
对于重载函数,只能使用lambda或者函数指针,指定初哪一个是需要的
类型转换运算符2
- 它是类的一种特殊成员函数,负责将一个类类型的值转换成其他类型。
- 一般形式: operator type() const;
- 不允许转换成数组或者函数类型,但允许转换成指针(包括数组指针和函数指针)或者引用类型。
- 没有返回类型,也没有形参,必须是类成员函数,形参列表是空
- 通常是const的成员函数
例子:
class SmallInt
{
public:
SmallInt(int i = 0):val(i){}
SmallInt& operator=(const SmallInt& a){cout << "assign\n";}
operator int()const{return val;}
private:
int val = 0;
};
int main()
{
SmallInt obj;
obj = 4; //调用赋值运算符opeartor=,int 4转换为类类型SmallInt
cout << obj + 4; //调用类型转换运算符,SmallInt类类型Obj转换为Int
return 0;
}