一、运算符函数
在C++中会把运算符当作函数处理,一个表达式其实是调用了很多运算符函数完成计算的,这种特性对内建类型没有用,但是对自建类型数据,却可以进行个性化设计,提高代码的可读性和易用性,如:string类
String str;
str += “hehe”;
str1 == str2
运算符函数的格式:
单目运算符:
#o | o# 会被编译器解释为
成员函数:
[] o::operator#(void)
{
}
返回值不确定,唯一参数是调用者
全局函数:
[] operator#(O& o)
{
}
运算符成员函数、全局函数,只能实现一个,不能同时实现
双目运算符:
a#b
成员函数:
[] A::operator#(B& b)
{
}
全局函数:
[] operator#(A& a, B& b)
{
}
二、运算类的双目运算符
const T operator+(const T& b) const
{
return T(x+b.x);
}
const T operator+(const T& a, const T& b) const
{
return T(x+b.x);
}
友元:
在实现类的全局运算符函数时可能会用到类的私有成员,如果把私有成员改为public或实现get函数就打破了类的封装性,最好解决方案就是给运算符全局函数进行独家授权,这种授权我们叫做设置友元函数
在类中声明全局函数,在声明前加上friend关键字
#include <iostream>
using namespace std;
class Point
{
int x;
int y;
public:
Point(int x=0,int y=0):x(x),y(y) {}
const Point operator+(const Point& that)const // 成员函数:a+b
{
return Point(x+that.x,y+that.y);
}
friend const Point operator+(const Point& a, const Point& b);
};
const Point operator+(const Point& a, const Point& b) // 全局函数:a+b
{
return Point(a.x+b.x,a.y+b.y);
}
三、输入输出运算符
在C++ << >> 运算符不仅可以左移右移,它们还是cin和cout输入、输出运算符。
输出运算符:cout << a << endl;
由于<<运算符的调用者是cout,所以无法实现出成员运算函数。
friend ostream& operator<<(ostream& os, const Point& p);//类内定义
ostream& operator<<(ostream& os,const Point& p)
{
return os << "(" << p.x << "," << p.y << ")";
}
输入运算符:cin >> a >> b
由于输入、输出运算符是由cin、cout调用的,我们无法在ostream、istream类中添加代码,因此只能实现全局运算符函数。
friend istream& operator>>(istream& is, Point& p);//类内定义
istream& operator>>(istream& is, Point& p)
{
cout << "请输入Point成员x值:";
is >> p.x;
cout << "请输入Point成员y值:";
is >> p.y;
return is;
}
四、运算类的单目运算符
成员函数:- ! ~
运算对象可以是常量,运算过程中并不会修改自身的值,而是产生一个临时的计算结果。
五、自变运算符
前自变运算符:
++i/–i
成员函数:
const Point& operator++(void)
{
x++,y++;
return *this;
}
全局函数:
friend const Point& operator++(Point& p);
const Point& operator++(Point& p)
{
p.x++,p.y++;
return *this;
}
后自变运算符:
i++/i–
成员函数:在参数列表中添加哑元与前自变区分。
const Point operator++(int)
{
return Point(x++,y++);
}
全局函数:
friend const Point operator++(Point& p, int);
const Point operator++(Point& p, int)
{
return Point(p.x++,p.y++);
}
六、特殊的运算符函数
1、下标运算符[]
当想让一个类对象当数组使用时,可以重载下载运算符,如标准模板库中的vector、list。
#include <iostream>
using namespace std;
#define TYPE int
class Array
{
TYPE* ptr;
int cal;
public:
Array(int cal):cal(cal)
{
ptr = new TYPE[cal];
}
TYPE& operator[](int i)
{
return ptr[i];
}
};
int main(int argc,const char* argv[])
{
Array arr(10);
for(int i=0; i<10; i++)
{
arr[i] = i;
cout << arr[i] << endl;
}
}
2、函数运算符() 重载此运算符可以让一个类对象当函数使用。
3、解引用和间接成员访问运算符* -> 重载此运算符可以让一个类对象像指针一样操作,智能指针就重载此运算符实现的。
4、new/delete的重载
为什么重载new/delete?
1、可以运算符函数中记录每次分配、释放的内存的地址检查是否有内存泄漏。
2、对于字节数较小、且频繁分配、释放的对象,可以多给它分配一些内存防止出现内存碎片。
成员函数与全局函数的格式相同:new TYPE;
void* operator new(size_t size)
{
void* ptr = malloc(size);
cout << "malloc " << ptr << endl;
return ptr;
}
void operator delete(void* ptr)
{
cout << "free " << ptr << endl;
free(ptr);
}
如果只针对某个类,则实现成员运算符函数即可,如果相所有的类型都可以使用,则实现全局函数。
七、智能指针
常规指针的缺点:
当一个常规指针离开作用域时,只有该指针本身所占用内存空间(4字节或8字节)会被释放,而它指向的内存空间不会被释放,当free、delete、delete[]语句无法执行、或忘记执行时就形成了内存泄漏。
智能指针的优点:
智能指针是一个封装了常规指针的类类型对象,当它离开作用域时,它的析构函数会自动释放常规则指针所指向的内存,防止内存泄漏,这样就做到了自动释放。
智能指针与常规指针的相同点:
智能指针虽然是类对象但它重载了 *、-> 可以像常规则指针一样正常使用。
智能指针的缺点:
1、不能跨作用域指针
2、不能多个指针指向一个对象,否则就会重载释放导致内存崩溃。
3、不能指向对象数组
4、不能放入容器中
C++中智能指针的使用方法:
#include < memory>
auto_ptr<类型> ptr(new 类型[100]);
ptr就可以像普通指针一样使用了。
#include <iostream>
#include <memory>
using namespace std;
class Test
{
int num;
public:
Test(int num):num(num) {}
~Test(void)
{
cout << "我被西狗了" << endl;
}
void show(void)
{
cout << "num = " << num << endl;
}
};
int main(int argc,const char* argv[])
{
auto_ptr<Test> ptr(new Test(100));
ptr->show();
}
int类型智能指针实现
#include <iostream>
using namespace std;
class IntPtr
{
int* ptr;
public:
IntPtr(int* ptr):ptr(ptr){}
~IntPtr(void)
{
delete ptr;
}
int& operator*(void)
{
return *ptr;
}
int& operator[](int i)
{
return ptr[i];
}
friend ostream& operator<<(ostream& os, const IntPtr& p);
};
ostream& operator<<(ostream& os, const IntPtr& p)
{
return os << p.ptr;
}
int main(int argc,const char* argv[])
{
IntPtr p(new int(100));
cout << p << endl;
cout << *p << endl;
*p = 1234;
cout << p[0] << endl;
}
八、重载运算符函数的限制
1、有些运算不能重载
:: 作用域限定符
. 直接成员访问操作符
?: 三目运算符
sizeof 字节长度操作符
typeid 类型信息操作符
2、只能重载为全局函数的运算符
<< 输入运算符 >> 输出运算符
3、只能重载为成员函数的运算符
[] 下标运算符 = 赋值运算符 -> 间接成员访问运算符 () 类型转换运算符
4、运算符重载可以自动定义运算符函数的计算过程,但是无法改变运算符的优先级。
5、运算符重载不能改变操作数的个数。
6、不能发明新的运算符
九、重载运算符要注意的问题
1、重载运算符时要遵循一致,不要改变运算符的运算规则。
2、重载运算符是为了提高代码的高读性(符合情理),不要炫技。