重载运算符(友元 哑元)

一、运算符函数

在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、重载运算符是为了提高代码的高读性(符合情理),不要炫技。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值