文章目录
轻松地使用这种语言。不要觉得必须使用所有的特性,不要在第一次学习时就试图使用所有的特性
0 日志
2022/04/18 起笔
2022/04/21 补充修改2.1友元引入原因
1 运算符重载
operatorop(argument -list) 注意op必须是有效的C++运算符
1.1 重载举例
1)函数原型的区别
Time Sum(const Time & t) const;
Time operator+(const Time & t) const;
2)函数定义的区别
Time Time::Sum(const Time & t) const{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = houts + t.hours + sum.minutes/60;
sum.minutes %= 60;
return sum;
}
Time Time::operator+(const Time & t) const{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + t.hours+ sum.minutes/60;
sum.minutes %= 60;
return sum;
}
3)函数调用的区别
total = coding.sum(fixing);
total = coding + fixing; 运算符表示法
total = coding.operator+(fixing); 函数表示法
4)链式表达
t4 = t1.sum(t2.sum(t3));
t4 = t1 + t2 + t3;
t4 = t1.operator+(t2+t3);
t4 = t1.operator+(t2.operator+(t3)); +是从左到右结合的运算符
1.2 重载限制
- 至少有一个操作数是用户定义的类型(防止用户为标准类型重载运算符)
- 不能违反运算符原来的句法规则(不能将%运算符重载为使用也给操作数)
- 不能修改运算符的优先级
C++中运算符的优先级 - 不能创建新的运算符
- 不能重载的运算符
sizeof
. 成员运算符
.* 成员指针运算符
:: 作用域解析运算符
?: 条件运算符
typeid 一个RTTI运算符(Run-Time Type Identification,运行时类型识别)
const_cast 以下皆为强制类型转换运算符
dynamic_cast
reinterpret_cast
static_cst
- 只能通过成员函数重载的运算符
= 赋值运算符
() 函数调用运算符
[] 下标运算符
-> 通过指针访问类成员的运算符
1.2.1 有的运算符只能通过成员函数重载?
2 友元
2.1 引入原因
首先介绍分类:
- 友元函数
- 友元类
- 友元成员函数
为何需要友元:
重载运算符*时,若为普通的成员函数调用,需要注意参数的顺序
A=B*2.75;
A=2.75*B;
无法兼得
解决方案:告诉每个使用的人只能按一种格式使用
即服务器友好-客户警惕的方案 server-friendly,elinet-beware
而非成员函数调用匹配,其不是由对象调用的
其使用的所有值都是显式参数
可以按所需的顺序调用匹配
Time operator*(double m, const Time & t);
A = operator*(2.75, B);
或者
A =2.75*B;
但问题:非成员函数不能直接访问类的私有数据
故产生了友元函数
2.2 创建友元
1)原型放入类声明中,并在前加上关键字friend
friend Time operator*(double m, const Time & t);
2)编写函数定义,注意不需要::限定符,不要使用关键字friend
Time operator*(double m , const Time & t){
Time result;
long totalminutes = t.hours * m * 60 + t.minutes * m;
注意这里访问了对象的私有成员
...
}
类的友元函数是非成员函数,访问权限与成员函数相同
通过使用友元函数和类方法,可以用同一个用户接口表达两种操作,它们只是表达类接口的两种不同机制
实际上,也可以将友元函数编写为非友元函数
Time operator*(double m, Time t){
return t*m;
}
原来的版本显式访问t.minutes等等,必须是友元,这个版本让成员函数处理私有值
因此不必是友元
而将此版本作为友元函数也不错,它将作为正式类接口的组成部分
以后如果发现需要函数直接访问私有数据,则只要修改函数定义即可,不必修改类原型
2.2.1 重载<<运算符
背景知识:cout是一个ostream对象,对于每种基本类型,ostream类声明中都包含了相应的重载的operator<<()定义
目的:使cout能识别对象
2.2.1.1 版本一:必须使用友元函数
原因:如果使用Time成员函数,Time对象将是第一个操作数
trip<<cout; 太诡异了
正确重载:
void operator<<(ostream & os,const Time & t){
os<<t.hous<<"hours,"<<t.minutes<<" minutes,";
}
注意这里:
1)应该使用cout对象本身,而不是拷贝
2)通常情况下,os引用cout对象
使用:
cout<<trip;
注意在这里由于并不直接访问ostream对象的私有成员,所以并不一定必须使ostream类的友元——这很好,意味着不必修订ostream的定义
2.2.1.2 版本二:返回ostream引用对象
原因:前一种版本不允许像通常那样将重定义运算符与cout一起使用
cout<<"//"<<trip;
<<运算符要求左边是一个ostream对象,ostream类中将operator<<()函数实现为返回一个指向ostream对象的引用
修改:
ostream & operator<<(ostream & os,const Time & t){
os<<t.hous<<"hours,"<<t.minutes<<" minutes,";
return os;
}
2.2.2 成员与非成员的选择
一般非成员函数应该是友元函数
非成员版本的重载运算符函数所需的形参数目与运算符使用的操作数数目相同
成员版本所需的参数数目少一个(其中一个操作数是被隐士第传递的调用对象)
在定义运算符 时,不能同时使用两种格式(通过友元函数反转参数的不算)
对于有些运算符,成员函数是唯一的选择
有时根据类设计,使用非成员函数版本可能更好
3 类的自动转化和强制类型转换?
C++可以自动转换兼容的类型,强制转换不兼容的类型