一、操作符函数重载
- 操作符函数(运算符函数)
- 在C++中针对类类型对象的运算符
- 符号本身不支持真正的运算操作
- 编译器把运算翻译成运算符函数
- 可以针对自定义的类类型设计它独有的运算功能。
- 操作符函数重载(运算符函数重载)
- 各种运算已经具备一些功能,再次实现它的就叫作运算符函数重载。
- 双目运算符
- a+b
- 成员函数:
a.operator+(b);
- 全局函数:
operator+(a,b);
- 单目运算符
- !a
- 成员函数:
a.operator!(void);
- 全局函数:
operator!(a);
二、双目操作符函数重载
const 类 operator#(const 类& that) const
{
return 类(参数#参数);
}
- 注意:双目运算符的运算结果是个右值,返回值应该加
const
,然后为了const
对象能够调用参数应写const
,函数也应该具备const
属性。
const 类 operator#(const 类& a,const 类& b)
{
}
- 注意:全局函数不是函数,可能会访问类的私有成员,解决这种问题可以把函数声明为类的友元函数(友元不是成员)。
- 友元:在类的外部某个函数中想访问类的私有成员(
public/protected/private
)时,需要所在的函数声明为友元,但友元只是朋友,因此它只有访问权,没有实际的拥有权(其根本原因是它没 this 指针)。 - 友元声明:把函数的声明写一份到类中,然后在声明前加上
friend
关键字,使用友元既可把操作符函数定义为全局的,也可以确保类的封装性。 - 注意:友元函数与成员函数不会构成重载关系,因为它们不在同一个作用域内。
三、赋值类型的双目操作符
类& operator#(const 类& that)
{
}
类& operator#(cosnt 类& a,const 类& b)
{
}
- 获取单参构造赋值运算的调用方式
String str = "sunll";
// 会调用单参构造,而不调用赋值运算符str = "hehe";
- 左操作数据不能具有
const
属性
- 成员函数不能是常函数
- 全局函数第一个参数不能有
const
属性
四、单目操作符函数重载
const 类 operator#(void) const
{
}
const 类 operator#(const 类& that)
{
}
类& operator#(void)
{
}
类& operator#(类& that)
{
}
const 类& operator#(int)
{
}
const 类& operator#(类& that,int)
{
}
五、输入输出操作符重载
cont
是 ostream
类型的对象cin
是 istream
类型的对象。- 如果 <</>> 运算实现为成员函数,那么调用者应该是ostream/istream,而我们无权增加标准库的代码,因此输人/输出运算符只能定义为全局函数。
ostream& operator<<(ostream& os,const 类& p)
{
}
istream& operator>>(istream& is,类& p)
{
}
- 注意:在输入输出过程中,cin/cout会记录错误标志,因此不能加
const
属性。
六、特殊操作符
类型& operator[ ](int i)
{
}
- 函数操作符 ( )
- 一个类如果重载函数操作符,那么它的对象就可以像函数一样使用,参数的个数和返回值类可以不确定,这是唯一一个可以有缺省参数的操作符
- 解引用操作符*
- 如果一个类重载了
*
,那么它的对象就可以像指针一样使用
- 成员访问操作符 ->
- 如果一个类重载了
->
,那么它的对象可以像指针一样使用
- 智能指针
- 所谓的智能指针就是一个类对象,它支持解引用和成员访问操作符(指针都支持*和->运算)
- 智能指针是一个封装了常规指针的类类型对象,当它离开作用域时,它的析构函数就会自动执行,释放常规指针指向的动态内存(以正确方式创建的智能指针,它的析构函数才能正确执行)
- 常规指针的缺点
- 当一个常规指针离开它的作用域时,只有该指针所占用的空间被释放,而它指向的内存空间不一定会被释放,在一些特殊情况(人为、业务逻辑的特殊要求)下,free或delete没有执行,就会造成内存泄漏
- 智能指针与常规指针的不同点
- 任何时候,一个对象只能使用一个智能指针来指向,而常规指针可以指向多次
- 智能指针的赋值操作需要经过拷贝构造和赋值构造特殊处理(深拷贝)
- 标准库中的智能指针
auto_ptr
是标准库中封装好的智能指针,实现了常规指针的基本功能- 头文件
#include < memory >
- 用法:
auto_ptr<指向的类型> 指针变量名(对象的地址)
- 局限性:
- 不能跨作用域使用,一旦离开作用域指针变量会释放,指向的对象也会释放
- 不能放入标准容器
- 不能指向对象数据
- new / delete / new[] / delete[] 运算符重载特点
- C++缺省的堆内存管理器速度较慢,重载后,底层调用malloc/free可以提高运行速度
- new在失败后会产生异常,而每次使用new时为了安全都应该进行异常捕获,而重载new操作符只需要在操作符函数中进行一次错误处理即可
- 一些占字节数据比较小的类,频繁使用new,可能会产生大量的内存碎片,而重载new操作后,可以适当的扩大每次申请的字节,减少内存碎片产生的几率
- 可以记录栈内存使用的信息
- 可以检查到释放内存失败时的信息,检查到内存泄漏
七、重载操作符的限制
- 不能重载的操作符
- 预限定符
::
- 直接成员访问操作符
.
- 三目操作符
?:
- 字节长度操作符
sizeof
- 类型信息操作符
typeid
- 限制规则
- 重载操作符不能修改操作符的优先级
- 无法重载所有基本类型的操作符运算
- 不能修改操作的参数个数
- 不能发明新的操作符
关于操作符重载的建议:
- 在重载操作符时要根据操作符实际的功能和意义来确定具体参数,返回值,是否具有 const 属性,返回值是否是引用或者是临时对象。
- 重载操作符要符合情理(要有意义),要以实际用途为前提前。
- 重载操作符的意义是为了让对象的操作更简单、方便,而不是为了炫技。
- 重载操作符要与默认的操作符的功能、运算规则一致,不要出反人类的操作