【C++】day05 - 【运算符重载】【友元函数】【二元运算符】【一元运算符】【运算符重载的限制】【new和delete重载】

一、运算符重载(二元运算符)

1.1概念

	是函数的特殊表现形式

1.2目的

	为了方便编程
	代码更加直观

1.3程序举例

	设计一个类,叫分数类
	分数类特征:
		分子
		分母
	分数类功能:
		显示分数
		运算:
			+   Fraction add(){}
			-
			*
			/
	全局区定义一个函数,代表两个分数相加
#include <iostream>
using namespace std;
class Fraction{
    
     
	public:
	int x;
	int y;
	Fraction(int x=0,int y=1):x(x),y(y){
    
    
		
	}
	void show(){
    
    
		cout << x << '/' << y << endl;
	}
};
/*设计一个函数,完成两个分数相加
b/a + d/c = (b*c + a*d)/(a*c)
*/
Fraction addFraction(const Fraction& fa,
const Fraction& fb){
    
    
	/*Fraction fc;
	fc.x = fa.x*fb.y + fa.y*fb.x;
	fc.y = fa.y*fb.y;
	return fc;*/
	return Fraction(fa.x*fb.y + fa.y*fb.x,fa.y*fb.y);
} 
int main(){
    
    
		Fraction fa(1,3);
		fa.show();
		Fraction fb(1,2);
		fb.show();
		
		Fraction fc = addFraction(fa,fb);
		/*注意、注意、注意:
		这样写固然可以,但我们不想这样写,我们写成下一条指令的
	形式。这就涉及到了运算符重载,去看1.4节来讲解运算符重载原理。
		然后再去看例1.5节的对本例的改进
		Fraction fc = fa+fb;
		*/
		fc.show();
}

1.4 运算符重载原理fa+fb讲解

		Fraction fa;
		Fraction fb;
		fa + fb;   
		遇到fa+fb系统解析的过程:				
			首先去类的成员函数中找一个成员函数
				operator+(const Fraction& fb)
					/*"operator+"是函数名*/
			fa + fb;   ---> fa.operator+(const Fraction& fb)
					/*即把fb当成fa.operator+()的参数*/
			
			若找不到,则去全局区找一个全局函数
				operator+(const Fraction& fa,const Fraction& fb);
	
	注意:你把函数名operation+换成operation-就完成两个数的相减
							换成operation*就完成两个数的相乘
							换成operation/就完成两个数的相除

1.5对1.3运用运算重载符fa+fb进行改进(全局形式)

		把函数名addFraction改为operator+即可	
#include <iostream>
......
Fraction /*addFraction*/operator+(const Fraction& fa,
const Fraction& fb){
    
    
	......
} 
int main(){
    
    
	......
	Fraction fc = fa+fb;
	......
}

1.6对1.3运用运算重载符fa+fb进行改进(成员函数形式)

#include <iostream>
......
class Fraction{
    
    
	......
	Fraction operator+(const Fraction& fb){
    
    
		return Fraction(this->x*fb.y + 
		this->y*fb.x,this->y*fb.y);
	} 
};
int main(){
    
    
	......
	Fraction fc = fa+fb;
	......
}

1.7 运算符重载fa*=fb

		Fraction fa;
		Fraction fb;
		fa *= fb;   
		程序如下:
#include <iostream>
......
class Fraction{
    
    
	......
	/*设计成员函数,完成两个分数*= */
	void operator*=(const Fraction& fb){
    
    
		this->x*=fb.x;
		this->y*=fb.y;
	} 
};
/*如果是全局函数:
void operator*=(Fraction& fa,const Fraction& fb){
		fa.x*=fb.x;
		fa.y*=fb.y;
}*/

int main(){
    
    
	......
	fa*=fb;
	fa.show();
	......
}	

1.7例子:分数和整数相加

	返回一个double,让一个分数和一个整数相加
#include <iostream>
......
/*返回一个double,让一个分数和一个整数相加*/
double operator+(const Fraction& f,int fb){
    
    
	return 1.0*f.x/f.y + x;
} 
int main(){
    
    
		......
		double res = fb+100;
		cout << res << endl;
}	

1.8友元函数

	在说友元函数之前,请你先看1.8.1,然后再看1.8.2了解友元函数
	设计一个整数包装类

1.8.1 方式一:使用公开的访问接口

	使用公开的访问接口,即使用函数int getData()const(){}
		达到访问私有成员data的目的。
#include <iostream>
using namespace std;
class Integer{
    
     
	int data;
	public:
	Integer(int data=0):data(data){
    
    
		
	}
	void show(){
    
    
		cout << data << endl;
	}
	Integer operator+(const Integer& i){
    
    
		return Integer(data + i.data);
	}
	public:
	/*提供对私有数据的访问接口*/
	/*这是本例的目的*/
	int getData()const{
    
    
		return data;
	}
		
};
/*写一个全局函数,完成两个Integer相减*/
Integer operator-(const Iconst& ia,const Integer& ib){
    
    
		return Integer(ia.getData() - ib.getData());
}
int main(){
    
    
	Integer ia(100);
	Integer ib(1);
	ia.show();
	ib.show();
	Integer ic = ia+ib;
	ic.show();
	Integer id = ia-ib;
	id.show();
}

1.8.2方式二:友元函数

	友元函数的本质就是全局,但友元函数获得了对私有成员的访问
		权力。
	你可以把友元函数理解为类的好朋友,它虽然不属于类的成员函数
		但是他可以访问类的私有成员。两个人的关系比较好嘛。
#include <iostream>
using namespace std;
class Integer{
    
     
	int data;
	public:
	Integer(int data=0):data(data){
    
    
	}
	void show(){
    
    
		cout << data << endl;
	} 
	/*在类内声明友元函数*/
	friend Integer operator-(const Iconst& ia,const Integer& ib);
	
};
/*写一个全局函数,完成两个Integer相减*/
Integer operator-(const Iconst& ia,const Integer& ib){
    
    
	return Integer(ia.data - ib.data);
	/*这里就可以直接使用ia.data,即直接访问类私有成员*/
}
int main(){
    
    
	Integer ia(100);
	Integer ib(1);
	Integer id = ia-ib;
	id.show();
}

1.9成员函数、静态函数、友元函数特性

	对类的私有成员的访问
	受类型作用域 和 权限限制
	需要通过对象去访问
		成员函数具有以上三个特性
		静态函数具有以上前两个特性
		友元函数只有第一个特性

1.10两个特殊的运算符

	<<
	>>
	Integer ia;
	cout << ia << endl;
	cout ------ osteram&
	注意:流类型的对象不能拷贝、不能加const修饰	
	
	cout << ia的解析:
		先去ostream类中,找一个成员函数,叫
			operator<<(const Integer& i)
			当然,这不现实,因为头文件ostream里的operator<<函数
			没有Inteher,Integer是你自己命名的。
			因此这种方法不现实
		这时,就会去全局区找一个全局函数,叫:
			operator<<(ostream& os,const Integer& i);
	输出流-程序举例:
#include <iostream>
	using namespace std;
	class Integer{
    
     
		int data;
		public:
		Integer(int data=0):data(data){
    
    	
		}
		void show(){
    
    
			cout << data << endl;
		}
		Integer operator+(const Integer& i){
    
    
			return Integer(data + i.data);
		}
		
		/*在类中声明友元函数*/
		friend Integer operator-(const Iconst& ia,const Integer& ib);
		/*在类中声明友元函数*/
		friend ostream& operator<<(ostream& os,const Integer& i);
		
	};
	/*写一个全局函数,完成两个Integer相减*/
	Integer operator-(const Iconst& ia,const Integer& ib){
    
    
		return Integer(ia.data - ib.data);
	}
	/*流输出函数*/
	ostream&operator<<(ostream& os,const Integer& i){
    
    
		os << i.data; 
		return os;
	}
	int main(){
    
    
		Integer ia(100);
		Integer ib(1);
		
		Integer ic = ia-ib;
		ic.show();
		cout << ic << endl;/*输出流*/
	}
补充一下 输入流函数:
	/*在类中声明友元函数*/
	friend istream& operator>>(istream& is,Integer& i)
	编写函数:
	istream& operator>>(istream& is,Integer& i){
		return is >> i.data;
	}

1.11其他的二元运算符

	== (以什么标准判断两个对象是否相等?)
		答:两个判断标准:值/地址
		int x=10;
		int y=10;
	== 程序举例:
#include <iostream>
using namespace std;
class Integer{
    
     
	int data;
	public:
	Integer(int data=0):data(data){
    
    	
	}
	void show(){
    
    
		cout << data << endl;
	}
	Integer operator+(const Integer& i){
    
    
		return Integer(data + i.data);
	}
	/*写一个成员函数,判断两个Integer相等*/
	bool operator==(const Integer& i){
    
    
		return data==i.data;//以值来判断两个对象是否相等
		//return this==&i;当然也可以用地址来判断
	}
};
int main(){
    
    
	Integer ia(100);
	Integer ib(100);
	
	if(ia == ib){
    
    
		cout << "ok" << endl;
	}
}

二、运算符重载(一元运算符)

2.1 一元运算符

	-(负号)  !  ~  ++   --
	使用方法:
		我们暂且用#a表示(#表示一元运算符、a表示对象)
	#a的解析过程:
		首先去a对象对应的类型中找一个成员函数operator#()
		找不到的话,就去全局找一个全局函数叫operator#(a)
				全局的话,你得把a传进去
	
	a#解析过程:(即运算符在对象后面)
		首先去a对象对应的类型中找一个
			成员函数operator#(int)
		找不到,就去全局区找一个全局函数叫
			operator#(a,int)

2.2 程序举例:

#include <iostream>
using namespace std;
class Integer{
    
     
	int data;
	public:
	Integer(int data=0):data(data){
    
    
		
	}
	Integer operator!(){
    
    
		return Integer(!data);
		/*如果一个类型中有一个单参的构造,则允许把这个
		单参类型自动转换成这个类类型,因此上面的语句
		可以用下面这个语句代替:
			return data;
		*/
	}
	Integer operator-(){
    
    
		return Integer(-data);
	}
	/*++默认是前加加*/
	Integer& operator++(){
    
    
		++data;
		return *this;
	}
	/*后++。注(后加加的连续加加没有意义,因此加上const)*/
	const Integer operator++(int){
    
    
		return Integer(data++);
	}
	friend ostream& operator<<(ostream& os,const Integer& i);
	friend Integer& operator--(Integer& i);
	friend const Integer operator--(Integer& i,int);
};
ostream& operator<<(ostream& os,const Integer& i){
    
    
		return os << i.data;
}

/*在全局实现前--和后--*/
Integer& operator--(Integer& i){
    
    
	--i.data;
	return i;
}
const Integer operator--(Integer& i,int){
    
    /*int的作用是让编译器知道这是后
++而不是前++*/
	return Integer(i.data--);
}
int main(){
    
    
	Integer ia(100);
	cout << !ia << endl;
	cout << -ia << endl;
	cout << ++ia << endl;//101
	cout << ++ia << endl;/*102,具有连加效果,因为
				你的operator++()函数返回值用了引用*/
	cout << ia++ << endl;//103
	cout << ia-- << endl;
	cout << --ia << endl;
}

三、运算符重载的限制

3.1不能重载的运算符

	::	作用域运算符
	.	成员运算符
	.*	成员指针解引用
	sizeof	计算类型大小的
	?	:		三元运算符
	typeid	获取类型信息(返回typeinfo)

3.2只能对已有的运算符进行重载

只能对已有的运算符进行重载,不能发明新的运算符

3.3不能仅对基本类型进行运算符重载

	运算符重载中至少有一个类型是非基本类型

3.4不能改变运算符的运算特性

	不能把一元改成二元的

3.5只能是重载成 成员形式的运算符号

	如  =(赋值运算符)(最好是成员的+=  -=  /=  ^=)
		[](取下标运算符)
		()
		*	->
	3.5这个太重要了,我们拿到第四章专门讲

四、只能是成员的运算符重载

4.1 = 和 [ ] 讲解

自定义数组 Array
	程序举例:(以=和[]为例)
#include <iostream>
using namespace std;
class Array{
    
    
	/*代表有多少个元素*/
	int size;
	/*这个数组最多能存储多少个元素*/
	int len;
	/*指向堆内存的指针*/
	int *datas;
	public:
	/*为防止由int直接变为Array类型,我们可以在构造函数前加
											explicit关键字*/
	Array(int len = 5):size(0),len(len){
    
    
		datas = new int[len];//分配内存
	}
	~Array(){
    
    //释放内存
		delete[] datas;
		datas = NULL;
	}
	/*拷贝构造函数*/
	Array(const Array& a){
    
    //处理内存独立性
		cout << "Array(Array&)" << endl;
		size=a.size;
		len = a.len;
		/*根据a对象的堆内存大小 重新申请空间*/
		datas = new int[len];
		/*复制a中的堆内存中的数据*/
		for(int i=0;i<size;i++){
    
    
			datas[i] = a.datas[i];
		}
	} 
	/*提供一个函数,每次向数组中加入一个元素*/
	void push_back(int da){
    
    
		if(size >= len){
    
    
			/*可以扩容数组空间*/
			expend();
		}
		datas[size++]=da;
		//size++;
	}
	void expend(){
    
    //扩容函数
		int *temp = datas;
		len = 2*len + 1;
		datas = new int[len];//重新申请一个大的内存
		for(int i=0;i<size;i++){
    
    
			datas[i] = temp[i];
		}
		delete[] temp//释放掉原来的
	}
	/*展现数据*/
	void show(){
    
    
		if(size == 0){
    
    
			cout << "[]" << endl;
			return;
		}
		cout << "[";
		for(int i =0;i<size-1;i++){
    
    
			cout << datas[i] << ",";
		}
		cout << datas[size-1] << "]" << endl;
	}
	Array& operator=(const Array& arr){
    
    
		if(this!=&arr/*防止自己赋值给自己*/){
    
    
			size=arr.size;
			len=arr.len;
			int *temp = datas;
			datas = new int[len];/*根据arr大小重新申请新内存。
						因为,可能arra扩容了,而arrb没有扩容*/
			for(int i=0;i<size;i++){
    
    
				datas[i] = arr.datas[i];
			}
			/*释放掉原来自己的内存*/
			delete[] temp;
		}
		return *this;
	}
	/*[] 根据下标取数据*/
	int operator[](int ind)const{
    
    
		return dadas[ind];
	}
};
void foo(){
    
    
	/*const*/Array arra;//默认有5个元素
	arra.push_back(9);//放数据
	arra.push_back(5);
	arra.push_back(2);
	arra.push_back(7);
	arra.push_back(2);
	arra.push_back(2);//扩容
	cout << "-----------" << endl;
	cout << arra[0] << endl;//下标运算符,需要写operator[]()函数
	
	/*Array arrb = arra;这样写就调用拷贝构造函数了,但是我们写成
						下面的语句呢*/
	Array arrb;
	arrb = arra;/*这个其实是调用operator=()函数,而不是调用拷贝
			构造函数。我们需要写一个成员函数operator()函数才行 */
	arrb.show();
}
int main(){
    
    
	foo();
}
一个练习

#include <iostream>
using namespace std;
class Integer{
    
     
	int *data;
	public:
	Integer(int data){
    
    
		this->data = new int(data);
	}
	~Integer(){
    
    
		delete data;
	}
	Integer(const Integer& i){
    
    
		data=new int(*(i.data));
	}
	Integer& operator=(){
    
    
		if(this!=&i){
    
    
			*data = *(i.data);
		}
		return *this;
	}
	friend ostream& operator<<(ostream& os,const Integer& i){
    
    
		return os << *(i.data);
	}
};
int main(){
    
    
	Integer ia=100;
	Integer ib=ia;//拷贝构造
	cout << ib << endl;
	Integer ic;
	ic=ib;//operator<<()函数
	cout << ic << endl;		
}

4.2 ()讲解

	()运算符作用:
		①负责类型转换
			把当前对象类型变成一个类型。
			operator 类型 (){
				return 类型的对象;
			}
		②函数对象(可以像函数一样去使用的对象)
			返回值类型 operator()(参数列表 ){
				return 值;
			}
	程序举例:
#include <iostream>
using namespace std;
class Product{
    
     
	int count;
	double price;
	public:
	Product(int count=0,double price=0.0)
	:count(count),price(price){
    
    	
	}
	operator int (){
    
    //相当于int operator()(){}
		return count;//返回类中的count
	}
	operator double (){
    
    //相当于double operator()(){}
		return price; 
	}
	/*函数对象*/
	double operator()(int c,double p){
    
    
		return c*p;
	}	
};
int main(){
    
    
	Product product(100,1.15);
	int count=(int)product;//得有operator int (){}函数
	cout << count << endl; //打印出100
	double price = (double)product;//得有operator double (){}函数
	cout << price << endl;
	product(1,2.5);/*看起来像函数调用吧,虽然product明明是一
	个对象。但其实是可以当成函数调用的,我们写一个
	operator函数就行了*/
	cout << product(1,2.5) << endl;//打印product()的返回值
}

4.3 * 和 ->讲解

	把一个不是指针的类型,当作指针类型来使用。(这就得用运算
		重载符了)
	程序举例:
#include <iostream>
using namespace std;
class A{
    
     
	public:
	A(){
    
    
		cout << "A()" << endl;
	}
	~A(){
    
    
		cout << "~A()" << endl;
	}
	void showa(){
    
    
		cout << "this A showa()" << endl;
	}
};
class myauto_ptr{
    
    //自动管理指针
	A *data;
	public:
	myauto_ptr(A *data=NULL):data(data){
    
    
		
	}
	~myauto_ptr(){
    
    
		delete  data;
	}
	void show(){
    
    
		cout << "this is auto_ptr show()"
	}
	/*重载 ->  获取管理的对象的地址*/
	A* operator->(){
    
    
		return data;
	}
	/*  重载*  获取管理的对象 */
	A& operator*(){
    
    
		return *data;
	}
};
void foo(){
    
    
	A *pa=new A();
	myauto_ptr mptr(pa);
	mptr.show();//调自己的show(),没问题
	 
	/* 下面三条语句是三种调用showa()的方法 */
	mptr->showa();/*mptr不是指针啊。但可以当成指针,需要重
			载运算符。
			但是我们写成下面的这个语句更好理解。两个语句是一样的*/
	mptr.operator->()->showa();
	(*mptr).showa();
}
int main(){
    
    
	foo();
}	

五、new和delete / new[]和delete[]重载

注:会new和delete的重载实现就行,new[]和delete[]就是在new和delete的基础上
加了一个[]

5.1 new和delete 与 malloc和free的比较

		new比malloc多做了什么?
		答:入如果类的成员是类类型,则自动创建这个成员
			自动处理类型转换
			自动调用构造函数
		delete比free多做了什么?	
			delete比free多调用了析构函数
	程序举例:
#include <iostream>
#include <cstdlib>
using namespace std;
class A{
    
    
	public:
	A(){
    
    
		cout << "A()" <<endl;
	}
	~A(){
    
    
		cout << "~A()" << endl;
	}
};
class B{
    
    
	A a;
	public:
	B(){
    
    
		cout << "B()" << endl;
	}
	~B(){
    
    
		cout << "~B()" << endl;
	}
};
int main(){
    
    
	B *pb = static_cast<B*>(malloc(sizeof(B)));//c语言中实现方法
	B *pb2 = new B();/*c++中实现方法,自动实现了那三件事:
		*/
	free(pb);//无析构调用
	delete pb2;//有析构调用
}

5.2重载new和delete

	void* operator new(size_t size) 
	void* operator delete(void* ptr);
	程序举例:
#include <iostream>
#include <cstdlib>
using namespace std;
class Date{
    
    
	int year;
	int month;
	int day;
	public:
	Date(int year=0,int month=0,int day =0)
	:year(year),month(month),day(day){
    
    
		cout << "Date(int,int,int)" << endl;
	}
	~Date(){
    
    
		cout << "~Date()" << endl;
	}
	static void *operator new(size_t size){
    
    
		cout << "my operator new" << size << endl;
		return malloc(size);
	}
	static void operator delete(void *ptr){
    
    
		cout << "operator delete" << endl;
		free(ptr);
	}
	
}
};
/*void *operator new(size_t size){
	cout << "my operator new" << size << endl;
	return malloc(size);
}*/
int main(){
    
    
	Date *date = new Date();//调用void *operator new(size_t size1)
							//同时也会调用Date(){}
	delete date;//调用static void operator delete(void *ptr)
							//同时也会调用~Date(){}
}

猜你喜欢

转载自blog.csdn.net/weixin_45519751/article/details/108297382