C++基础——运算符重载

1. 概念

1.1 什么是运算符重载

在这里插入图片描述
所谓重载,就是重新赋予新的含义。 函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是”一名多用”。

运算符也可以重载。实际上,我们已经在不知不觉之中使用了运算符重载。例如,大 家都已习惯于用加法运算符”+”对整数、单精度数和双精度数进行加法运算,如5+8, 5.8 +3.67等,其实计算机对整数、单精度数和双精度数的加法操作过程是很不相同的, 但由于C++已经对运算符”+”进行了重载,所以就能适用于int, float, doUble类型的运算。

又如”<<“是C++的位运算中的位移运算符(左移),但在输出操作中又是与流对 象cout 配合使用的流插入运算符,”>>“也是位移运算符(右移),但在输入操作中又是与流对象 cin 配合使用的流提取运算符。这就是运算符重载(operator overloading)。C++系统对”<<“和”>>“进行了重载,用户在不同的场合下使用它们时,作用是不同 的。对”<<“和”>>“的重载处理是放在头文件stream中的。因此,如果要在程序中用”<< “和”>>”作流插入运算符和流提取运算符,必须在本文件模块中包含头文件stream(当然还应当包括”using namespace std“)。

1.2 运算符重载入门技术推演

  1. 为什么会用运算符重载机制
    用复数类举例
    //Complex c3 = c1 + c2;
    //原因 Complex是用户自定义类型,编译器根本不知道如何进行加减
    //编译器给提供了一种机制,让用户自己去完成,自定义类型的加减操作。。。。。
    //这个机制就是运算符重载机制
  2. 运算符重载的本质是一个函数
#include "iostream"
using namespace std;

class Complex
{
public:
	int a;
	int b;
public:
	Complex(int a=0,int b=0)
	{
		this->a=a;
		this->b=b;
	}
	void printCom()
	{
		cout<<a<<" + "<<b<<"i"<<endl;
	}
};

//定义了全局函数
Complex myAddd(Complex &c1,Complex &c2)
{
	Complex tmp(c1.a+c2.a,c1.b+c2.b);
	return tmp; 
}

//
Complex operator+(Complex &c1,Complex &c2)
{
	Complex tmp(c1.a+c2.a,c1.b+c2.b);
	return tmp; 
}



void main()
{
	int a=0,b=0;
	int c;
	c=a+b;   //1   基础数据类型  编译器已经知道了该如何运算

	//a+bi 复数运算规则
	Complex c1(1,2),c2(3,4);
	Complex c3;  //2   类  也是一种数据类型   用户自定义数据类型  C++编译器  是不知道如何进行运算的
	
	//c3=c1+c2;

	//3   C++编译器应该给我们程序员提供一种机制...
	//让自定义数据类型   有机会 进行  运算符操作=====》运算符重载机制

	//4  运算符重载机制

	//步骤1
	/*Complex c4 = myAddd(c1, c2);
	c4.printCom();*/

	//步骤2   //complex c4=c1+c2
	/*Complex c4=operator+(c1,c2);
	c4.printCom(); */

	//步骤3
	Complex c4=c1+c2;
	c4.printCom();

	//总结1: 运算符重载的本质  是  函数的调用


	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

2. 运符重载的限制

在这里插入图片描述
在这里插入图片描述

3. 运算符重载编程基础

在这里插入图片描述

//全局函数 完成 +操作符 重载
Complex operator+(Complex &c1, Complex &c2)
//类成员函数 完成 -操作符 重载
Complex operator-(Complex &c2)

3.1 运算符重载的两种方法

在这里插入图片描述
在这里插入图片描述
例如1:

/通过类成员函数完成-操作符重载
//函数声明 Complex operator-(Complex &c2)
//函数调用分析
//用类成员函数实现-运算符重载
	Complex c4 = c1 - c2;
	c4.printCom();
	//c1.operator-(c2);

例如2:

//通过全局函数方法完成+操作符重载
//函数声明 Complex operator+(Complex &c1, Complex &c2) 
//函数调用分析
int main()
{
		Complex c1(1, 2), c2(3, 4);
		//Complex c31 = operator+(c1, c2);
Complex c3 = c1 + c2; 
c3.printCom();
}

在这里插入图片描述
例如3

//前置++操作符 用全局函数实现 
Complex& operator++(Complex &c1) 
{
	c1.a ++;   
	c1.b ++;
	return c1;  
}
//调用方法
	++c1 ; //=需要写出操作符重载函数原形
	c1.printCom();

  • 运算符重载函数名定义:
    //首先承认操作符重载是一个函数 定义函数名 operator++
    //分析函数参数 根据左右操作数的个数, operator++(Complex &c1)
    //分析函数返回值 Complex& operator++(Complex &c1) 返回它自身
    例如4
//4.1前置—操作符 成员函数实现
Complex& operator--()
{
	this->a--;
	this->b--;
	return *this;
}

	//4.2调用方法
		--c1;
		c1.printCom();
	//4.3前置—运算符重载函数名定义
	//c1.operator--()

例如5:

//5.1 //后置++ 操作符 用全局函数实现
Complex operator++(Complex &c1, int) 
{
	Complex tmp = c1;
	c1.a++;
	c1.b++;
	return tmp;
}
//5.2 调用方法
c1 ++ ; //先使用 后++
//5.3 后置++运算符重载函数名定义
 Complex operator++(Complex &c1, int) //函数占位参数 和 前置++ 相区别

例如6

//6.1 后置— 操作符 用类成员函数实现
	Complex operator--(int) 
	{
		Complex tmp = *this;
		this->a--;
		this->b--;
		return tmp;
	}
//6.2 调用方法
c1 ++ ; //先使用 后++
//6.3 后置--运算符重载函数名定义
 Complex operator--(int) //函数占位参数 和 前置-- 相区别

前置和后置运算符总结:C++中通过一个占位参数(int(后置))来区分前置运算和后置运算

在这里插入图片描述

#include "iostream"
using namespace std;

class Complex
{
private:
	int a;
	int b;
	friend Complex operator+(Complex &c1,Complex &c2);
	friend Complex& operator++(Complex &c1);
	friend Complex operator++(Complex &c1,int );
public: 
	Complex(int a=0,int b=0)
	{
		this->a=a;
		this->b=b;
	}
	void printCom()
	{
		cout<<a<<" + "<<b<<"i"<<endl;
	}
public:
	Complex operator-(Complex &c2)
	{
		Complex tmp(this->a-c2.a,this->b-c2.b);
		return tmp; 
	}
	//前置--
	Complex &operator--()
	{
		this->a--;
		this->b--;
		return *this;
	}
	//后置--
	Complex operator--(int)
	{
		Complex tmp=*this;
		
		this->a--;
		this->b--;
		return tmp;
	}
};

//重载前置++
Complex& operator++(Complex &c1)
{
	c1.a++;
	c1.b++;
	return c1;
}
//后置++
Complex operator++(Complex &c1,int)
{
	//先使用,再让C1++
	Complex tmp=c1;
	/*return c1;*/
	c1.a++;
	c1.b++;
	return tmp; 
}

//全局函数法
Complex operator+(Complex &c1,Complex &c2)
{
	Complex tmp(c1.a+c2.a,c1.b+c2.b);
	return tmp; 
}

void main()
{
	Complex c1(1,2),c2(3,4);

	Complex c3=c1 + c2;
	c3.printCom();

	//1   全局函数方法实现 +  运算符重载
	//Complex operator+(Complex &c1,Complex &c2);

	//2  成员函数方法实现加号运算符重载
	//c1.operator-(this,c2);
	//Complex operator-(Complex &c2)
	Complex c4=c1 - c2;
	c4.printCom();

	//前置++操作符  全局
	/*++c1;
	Complex& operator++(Complex &c1);*/   //返回c1本身合适,返回一个引用
	++c1;
	c1.printCom();

	//前置--操作符  成员
	--c1;
	c1.printCom();

	//c1.operator--();
	//Complex &operator--();

	c1++;
	c1.printCom();
	//Complex operato r++(Complex &c1);
	
	//后置--操作符  用成员函数实现
	c1--;
	c1.printCom();
	//c1.operator--()
	//Complex operator--();

	cout<<"hello..."<<endl;
	system("pause");
	return ;
} 

3.2 定义运算符重载函数名的步骤

全局函数、类成员函数方法实现运算符重载步骤
1)要承认操作符重载是一个函数,写出函数名称operator+ ()
2)根据操作数,写出函数参数
3)根据业务,完善函数返回值(看函数是返回引用 还是指针 元素),及实现函数业务

3.3 友元函数实现操作符重载的应用场景

1)友元函数和成员函数选择方法

  • 当无法修改左操作数的类时,使用全局函数进行重载
  • =, [], ()和->操作符只能通过成员函数进行重载

2)用友元函数 重载 << >>操作符

  • istream 和 ostream 是 C++ 的预定义流类
  • cin 是 istream 的对象,cout 是 ostream 的对象
  • 运算符 << 由ostream 重载为插入操作,用于输出基本类型数据
  • 运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据
  • 用友员函数重载 << 和 >> ,输出和输入用户自定义的数据类型

a)用全局函数方法实现 << 操作符

ostream& operator<<(ostream &out, Complex &c1)
{
	//out<<"12345,生活真是苦"<<endl;
	out<<c1.a<<" + "<<c1.b<<"i "<<endl;
	return out;
}
//调用方法
cout<<c1;
//链式编程支持
cout<<c1<<"abcc";
//cout.operator<<(c1).operator<<("abcd");

//函数返回值充当左值 需要返回一个引用
b)类成员函数方法无法实现 << 操作符重载
//因拿到cout这个类的源码
//cout.operator<<(c1);
3)友元函数重载操作符使用注意点

a) 友员函数重载运算符常用于运算符的左右操作数类型不同的情况
在这里插入图片描述
b)其他

  • 在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择
  • 友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
  • C++中不能用友员函数重载的运算符有 = () [] ->
#include "iostream"
using namespace std;

class Complex
{
private:
	int a;
	int b;
	friend ostream& operator<<(ostream &out,Complex &c1); 
public: 
	Complex(int a=0,int b=0)
	{
		this->a=a;
		this->b=b;
	}
	void printCom()
	{
		cout<<a<<" + "<<b<<"i"<<endl;
	}
public:
	Complex operator-(Complex &c2)
	{
		Complex tmp(this->a-c2.a,this->b-c2.b);
		return tmp; 
	}
	//前置--
	Complex &operator--()
	{
		this->a--;
		this->b--;
		return *this;
	}
	//后置--
	Complex operator--(int)
	{
		Complex tmp=*this;
		
		this->a--;
		this->b--;
		return tmp;
	}
	//重载   前置++
	Complex& operator++()
	{
		this->a++;
		this->b++;
		return *this;
	}
	//后置++
	Complex operator++(int)
	{
		//先使用,再让C1++
		Complex tmp=*this;
		/*return c1;*/
		this->a++;
		this->b++;
		return tmp; 
	}

	//+
	Complex operator+(Complex &c2)
	{
		Complex tmp(this->a+c2.a,this->b+c2.b);
		return tmp; 
	}

};




void main31()
{
	Complex c1(1,2),c2(3,4);

	Complex c3=c1 + c2;
	c3.printCom();

	//1   全局函数方法实现 +  运算符重载
	//Complex operator+(Complex &c1,Complex &c2);

	//2  成员函数方法实现加号运算符重载
	//c1.operator-(this,c2);
	//Complex operator-(Complex &c2)
	Complex c4=c1 - c2;
	c4.printCom();

	//前置++操作符  全局
	/*++c1;
	Complex& operator++(Complex &c1);*/   //返回c1本身合适,返回一个引用
	++c1;
	c1.printCom();

	//前置--操作符  成员
	--c1;
	c1.printCom();

	//c1.operator--();
	//Complex &operator--();

	c1++;
	c1.printCom();
	//Complex operato r++(Complex &c1 );
	
	
	//后置--操作符  用成员函数实现
	c1--;
	c1.printCom();
	//c1.operator--()
	//Complex operator--();


	cout<<"hello..."<<endl;
	system("pause");
	return ;
} 
//void operator<<(ostream &out,Complex &c1) 
//{
//	out<<"12345  csdfdf"<<endl; 
//	out<<c1.a<<"+"<<c1.b<<"i"<<endl;
//}
ostream& operator<<(ostream &out,Complex &c1) 
{
	out<<"12345  csdfdf"<<endl; 
	out<<c1.a<<"+ "<<c1.b<<"i"<<endl;
	return out;
}

void main()
{
	int a=10;
	Complex c1(1,2),c2(3,4);
	cout<<a<<endl;

	//1
	cout<<c1;
	//2  ostream类中添加成员函数operator<<
	//cout.operator<<(c1);
	//输入输出流运算符重载必须用有函数
	//cout.operator 

	//2
	cout<<c1<<"aaaaddd";
	//


	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

4. 运算符重载提高

4.1 运算符重载机制

4.2 重载赋值运算符=

  • 赋值运算符重载用于对象数据的复制
  • operator= 必须重载为成员函数
  • 重载函数原型为:
类型 & 类名 :: operator= ( const 类名 & ) ;

#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>
using namespace std;

//

class  Name
{
public:
	Name(const char *myp)
	{
		m_len = strlen(myp);
		m_p =(char *) malloc(m_len + 1); //
		strcpy(m_p, myp);
	}

	//Name obj2 = obj1;
	//解决方案: 手工的编写拷贝构造函数 使用深copy
	Name(const Name& obj1)
	{
		m_len = obj1.m_len;
		m_p = (char *)malloc(m_len + 1);
		strcpy(m_p, obj1.m_p);
	}

	//obj3 = obj1 ;  // C++编译器提供的 等号操作 也属 浅拷贝
	//obj3.operator=(obj1)

	Name& operator=(Name &obj1)
	{
		//先释放旧的内存
		if (this->m_p != NULL)
		{
			delete[] m_p;
			m_len = 0;
		}
		//2 根据obj1分配内存大小
		this->m_len = obj1.m_len;
		this->m_p = new char [m_len+1];
		
		//把obj1赋值
		strcpy(m_p, obj1.m_p);
		return *this;
	}
	
	~Name()
	{
		if (m_p != NULL)
		{
			free(m_p);
			m_p = NULL;
			m_len = 0;
		}
	}
protected:
private:
	char *m_p ;
	int m_len; 
};

//对象析构的时候 出现coredump
void objplaymain()
{
	Name obj1("abcdefg");
	Name obj2 = obj1;  //C++编译器提供的 默认的copy构造函数  浅拷贝
	Name obj3("obj3");

	obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
	//obj3.operator=(obj1)
	//operato=(Name &obj1)

	obj1 = obj2 = obj3;
	//obj2.operator=(obj3);
	//obj1 = void;
}

void main()
{
	objplaymain();
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

结论:
1 //先释放旧的内存
2 返回一个引用
3 =操作符 从右向左

4.3 重载数组下表运算符[]

重载[]和()运算符

  • 运算符 [] 和 () 是二元运算符
  • [] 和 () 只能用成员函数重载,不能用友元函数重载

重载下标运算符 []
[] 运算符用于访问数据对象的元素
重载格式 类型 类 :: operator[] ( 类型 ) ;

设 x 是类 X 的一个对象,则表达式
x [ y ]
可被解释为
x . operator [ ] ( y )
在这里插入图片描述
在这里插入图片描述

4.4 重载函数调用符 ()

() 运算符用于函数调用

  • 重载格式 类型 类 :: operator() ( 表达式表 ) ;

例1
设 x 是类 X 的一个对象,则表达式
x ( arg1, arg2, … )
可被解释为
x . operator () (arg1, arg2, … )

例2:用重载()运算符实现数学函数的抽象

#include <iostream>
class  F
  { public :  
        double  operator ( )  ( double x ,  double  y ) ;
  } ;
double  F :: operator ( )  ( double  x ,  double  y )
   { return   x * x + y * y ; }
void main ( )			
{ 
F  f  ;
f.getA();
  	cout << f ( 5.2 , 2.5 ) << endl ;  // f . operator() (5.2, 2.5)
}

例3 用重载()运算符实现 pk 成员函数

#include <iostream.h>

class  F
{ 
public :  
	double  memFun ( double x ,  double  y ) ;
};

double  F :: memFun ( double  x ,  double  y )
{ return   x * x + y * y ; }

void main ()			
{ 
	F  f ;
   	cout << f.memFun ( 5.2 , 2.5 ) << endl ;
}

#include "iostream"
using namespace std;

class F
{
public:
	int operator()(int a,int b)
	{
		return a*a + b*b;
	}
};

class F2 
{
public:
	int MemFunc(int a,int b)
	{
		return a*a + b*b;
	}
};

void main()
{
	F f;
	f(2,4);
	cout<<f(2,4)<<endl;

	F2 f2;
	f2.MemFunc(2,4);
	//7
	//operator()(int a,int b)

	cout<<"hello..."<<endl;
	system("pause");
	return ;
}

4.5 为什么不要重载&&和||操作符

理论知识:

  • 1)&&和||是C++中非常特殊的操作符
  • 2)&&和||内置实现了短路规则
  • 3)操作符重载是靠函数重载来完成的
  • 4)操作数作为函数参数传递
  • 5)C++的函数参数都会被求值,无法实现短路规则
#include <cstdlib>
#include <iostream>

using namespace std;

class Test
{
	int i;
public:
	Test(int i)
	{
		this->i = i;
	}

	Test operator+ (const Test& obj)
	{
		Test ret(0);

		cout<<"执行+号重载函数"<<endl;
		ret.i = i + obj.i;
		return ret;
	}

	bool operator&& (const Test& obj)
	{
		cout<<"执行&&重载函数"<<endl;

		return i && obj.i;
	}
};

// && 从左向右
void main71()
{
	int a1 = 0;
	int a2 = 1;

	cout<<"注意:&&操作符的结合顺序是从左向右"<<endl;

	if( a1 && (a1 + a2) )
	{
		cout<<"有一个是假,则不在执行下一个表达式的计算"<<endl;
	}

	Test t1 = 0;
	Test t2 = 1;

	//if( t1 && (t1 + t2)  )
	//t1  && t1.operator+(t2)
	// t1.operator&&(  t1.operator+(t2) )   

	//1 && || 重载他们 不会产生短路效果
	if(  (t1 + t2) && t1)
	{
		//t1.operator+(t2) && t1;
		//(t1.operator+(t2)).operator&&(t1);
	
		cout<<"两个函数都被执行了,而且是先执行了+"<<endl;
	}

	//2 && 运算符的结合性
	// 两个逻辑与运算符  在一块的时候, 采去谈 运算符的结合性
	// 从左到右    (t1 + t2) && t1 ; 运算结果 && t2)
	if(  (t1 + t2) && t1 && t2)
	{
		//t1.operator+(t2) && t1;
		//(t1.operator+(t2)).operator&&(t1);

		cout<<"两个函数都被执行了,而且是先执行了+"<<endl;
	}

	system("pause");
	return ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值