标准C++
一、运算符函数
在C++中会把运算符当做函数处理,一个表达式,其实是调用了很多的运算符函数完成计算,
这种特性对于内建类型是没有意义的,但是对于自建类型的数据,可以进行个性化设计,,可以大大地提高代码可读性,易用性
运算符函数的格式:[#是运算符 O是类对象]
单目运算符:
成员函数:
[] O::operater#(void)
{
}
注意:返回值不确定,运算对象就是函数的调用者,没有参数
全局函数:
[] operater#(O& o)
{
// 全局运算函数不属于任何类,因此需要把调用者作为参数传递过来
}
注意:运算符成员函数、全局函数,只能实现一个,不能同时实现,编译器会产生歧义
双目运算符: a # b
注意:一定是运算符左边的对象发起的函数调用
成员函数:
[] A::operater#(B& b)
{
// 类A对象必须是运算符函数的调用者
}
全局函数:
[] operater#(A& a,B& b)
{
//函数调用者放第一个参数
}
二、运算类的双目运算符
class T
{
int x;
public:
//成员函数:a+b
const T operater+(const T& b)const
{
return T(x+b.x)
}
};
全局函数:
const T operater+(const T& a,const T& b)
{
return T(a.x+b.x); // x是私有的,无法直接类外访问
}
youyuan
友元:
在实现类的全局运算符函数时,可能会使用类内的私有成员,此时全局运算符函数是没有访问权限的,如果把私有
成员编程public会打破类的封装性,或者实现get函数会很麻烦,C++提供了友元这种方式来为全局运算符函数提供
独家授权,该函数就称为友元函数
方法1:
在类内对全局运算符函数进行声明,并且在声明前加friend 关键字即可
三、输入输入运算符
在C++中 << >> 运算符不光是按位左移,按位右移,同时它们还是cout \ cin 的输出、输入运算符
输出运算符:cout << 10 << endl;
Test t;
cout << t << endl;
此时 << 运算符的调用者是 cout,所以我们无法在
cout类中实现<< 运算符函数重载,只能实现 全局的
<< 运算符函数
ostream& operater<<(ostream& os,const Test& t)
{
return os << t.x; // 不建议换行
}
注意:使用了私有成员,需要声明全局输出运算符函数为友元
输入运算符:cin >> num;
Test a,b;
cin >> a >> b;
istream& operater>>(istream& is,Test& t)
{
return is >> t.x;
}
注意:
1、由于输入、输出是可以连续调用,因此返回值应该还是cin、cout,应该返回istream& ostream&
2、由于输入、输出运算符的调用者是左边的cin、cout,我们无法实现它们的成员运算符函数,只能实现全局的输入输出运算符函数
3、如果在全局运算符函数中使用了私有成员,需要声明全局运算符函数为友元函数
四、运算类的单目运算符
单目运算符: ++/-- !~
成员函数:!~ -
const Test operater-(void)const
{
return Test(-x,-y);
}
注意: 单目运算类 的运算符对象都可以带常属性,因此重载的单目运算符函数
必须是常函数,并且运算过程中都不会改变自身的值,而是产生一个临时的计算结果,并且是右值,
只能返回带const的临时对象
全局函数:
const Test operater-(const Test& t)
{
return Test(-t.x,-t.y);
}
五、自变运算符函数
前自变运算符:++i/–i
注意:在C语言中无论前、后自变得到的结果都是右值,但是在C++中前自变的结果是左值,后自变的结果是右值
int num = 10;
++(num++) C C++ 报错
(++num)++ C++可以 C报错
成员函数:
T& operater++(void)
{
x++,y++;
return *this; // 一定要返回引用
}
全局函数:
T& operater++(Test& t)
{
t.x++,t.y++;
return t; // 一定要返回引用
}
后自变运算符函数:i++/i–
哑元:在参数列表中增加一个不会使用的哑元类型参数,唯一目的就是为了区分前自变还是后自变(哑元的唯一用处)
成员函数:
const T operator++(int)
{
return T(x++,y++) // 必须返回临时对象,不能是引用
}
全局函数:
const T operator++(T& t,int)
{
return T(t.x++,t.y++); // 必须返回临时对象,不能是引用
}
注意:要会写
* & () []
六、特殊的运算符函数
1、下标运算符[]
当想让一个类对象当做数组使用时,可以考虑重载下标运算符,例如vector类中就重载了[]
2、函数运算符()
当想让一个类对象当做函数一样使用时,可以考虑重载该运算符
3、解引用* 和 访问成员运算符->
重载这两个运算符时可以让一个类对象像指针一样使用
C++的智能指针就是重载了它们来实现的
4、new和delete
为什么要重载new和delete运算符?
1、可以在重载该运算符函数时记录每次分配、释放内存的地址、时间、函数等
信息到日志中,可以检查哪里出现了内存泄漏、什么时候出现的
成员函数与全局函数格式一样:
void* operater new(size_t size)
{
// size是要申请内存的字节数,编译器会自动计算并传递过来
// 做一些自己的操作
void* ptr = malloc(size);
return ptr;
}
void* operater delete(void* ptr)
{
// 做一些自己的操作
free(ptr);
}
注意:如果只是针对某个类重载它的new\delete,那么只需要实现成员函数即可,如果想要所有类型进行new/delete时
都是用重载的函数,则实现为全局函数
七、重载运算符的规则
1、有些运算符是不能重载
:: 域限定符
. 直接访问成员的运算符
? : 三目运算符
sizeof 计算字节数的运算符
typeid 获取类型信息的运算符
2、只能重载成全局函数的运算符
<< 输出运算符:cout
>> 输入运算符
3、只能重载成成员函数的运算符
[] 下标运算符
() 函数运算符
= 赋值操作运算符 类内一定有一个成员函数
-> 间接访问成员的运算符
4、运算符重载可以自定义运算的过程,但是无法改变运算符的优先级
5、运算符的运算对象不能数量和格式不能改变
6、不能发明新的运算符
建议:
1、重载运算符要遵循运算符含义一致性,不要改变运算符的运算规则
2、不要忘记重载运算符初衷,不要炫技