IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【五、运算符重载实例分析】运算符重载实例、重载的机制、步骤、返回值及参数确定、友元函数与成员函数 -> 正文阅读

[C++知识库]【五、运算符重载实例分析】运算符重载实例、重载的机制、步骤、返回值及参数确定、友元函数与成员函数

前言

在C++中有很多运算符,有单目运算符、双目运算符、三目运算符等,运算符重载使我们可以根据自己的需求实现加减乘除等各种运算,是不是所有运算符都可以重载呢?重载的运算符函数如何去确定函数原型呢?重载的方法分为成员函数法和友元函数法,那么他们有什么不同呢?下面就通过具体的运算符重载程序来详细分析如何确认重载函数的原型,如何选择成员函数或友元函数。


运算符重载知识点总结

1. 运算符重载为我们提供了对自定义数据进行运算的机制,让我们可以自定义加减等运算;
2. 运算符也是一个函数,运算符重载的本质是函数重载;
3. 运算符重载不改变原优先级;
4. 运算符重载不改变原有的结合顺序(自左向右、自右向左);
5. 运算符重载不能改变操作数的个数;
6. 不能自己创建新的运算符;
7. 操作符. :: .* ?: sizeof不可重载;
8. 运算符重载重载有成员函数和友元函数两种,区别在于成员函数有this指针(可以减少一个函数参数),友元函数没有this指针;
9. =, [], ()和->操作符只能通过成员函数进行重载 ;
10. << >>左移右移只能用友元函数;

实例分析

详细的分析都在程序中,一定要多看程序、多写程序

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
using namespace std;

//操作符重载,操作符也是一个函数,我们可以根据需求对操作符重载,但是以下操作符不可重载
//  .   ::   .*   ?:   sizeof
//使用友元函数或成员函数重载操作符的本质是,友元函数没有this指针,成员函数有this指针
//因为 this 指针的存在,成员函数比友元函数少了一个参数
///
class PluralClass
{
public:
	PluralClass(int r = 0, int v = 0)
	{
		this->real = r;
		this->virt = v;
	}

	void print_data(void)
	{
		cout << real << "+" << virt << "i" << endl;
	}

public:
	//成员函数实现 - 号操作符 //二元操作符
	//调用语句   p2.operator-(p3);
	//1.先写出函数名 operator-()    
	//2.因为是类的成员函数,所以 - 操作符的左操作数转化为 this 指针并隐藏,只有一个函数参数即右操作数 operator-(PluralClass& p)
	//3.他应该返回一个匿名对象元素并转为 = 的左值
	PluralClass operator-(PluralClass& p)
	{
		PluralClass temp((this->real - p.real), (this->virt - p.virt));
		return temp;
	}

	PluralClass& operator--() //前置 --
	{
		this->real--;
		this->virt--;
		return *this;
	}

	PluralClass operator--(int) //后置 --
	{
		PluralClass temp = *this;
		this->real--;
		this->virt--;
		return temp;
	}

private:
	int real;
	int virt;

	friend PluralClass operator+(PluralClass& p1, PluralClass& p2); //友元函数不受 private public 的影响
	friend PluralClass& operator++(PluralClass& p); //前置++
	friend PluralClass operator++(PluralClass& p, int); //后置++
	friend ostream& operator<<(ostream& out, PluralClass& p);
};

//友元函数实现+重载 //二元操作符 //如果类的属性real virt是public就可以不用友元函数,直接使用全局函数即可
//友元函数就是为了在类的外部使用类的私有属性
//p4 = p2 + p3   先写出调用语句,然后进行操作符重载三部曲
//1.定义一个函数名为  operator+()  
//2.他应该有两个参数,把 + 的两个操作数传进去 operator+(PluralClass& p1, PluralClass& p2)
//3.他应该返回一个匿名对象元素并转为 = 的左值 //返回一个新的元素,用元素做返回值
//(判断返回值的时候,如果返回的是一个新的元素,就用 PluralClass 元素。如果返回的是操作数元素本身,就用引用PluralClass&)
PluralClass operator+(PluralClass& p1, PluralClass& p2)
{
	PluralClass temp((p1.real + p2.real), (p1.virt + p2.virt));
	//real和virt是类的私有属性,所以需要把该函数在类中声明为友元函数
	return temp;
}

//重载前置++
PluralClass& operator++(PluralClass& p) //返回的是函数参数本身,所以返回引用
{
	p.real++;
	p.virt++;
	return p;
}

//重载后置 ++ //先使用后++ 所以应返回一个新元素(没++的元素),而操作数已经加完了
PluralClass operator++(PluralClass& p, int) //和前置++函数参数相同,函数名相同,
{									 //而函数返回值不是判断函数重载的标准,怎么办?
									 //加占位符参数来区分,函数调用时不用管展位参数
	PluralClass temp = p; //调用默认拷贝构造函数//浅拷贝
	p.real++;
	p.virt++;
	return temp;
}

//使用友元函数和成员函数(优先使用成员函数)重载操作符入门
void FuncTest1(void)
{
	PluralClass p1; //构造函数中设置了默认参数,所以在定义对象的时候可以不加参数
	p1.print_data();

	PluralClass p2(1, 2), p3(1, 2);
	p2.print_data();
	p3.print_data();

	PluralClass p4;
	p4 = p2 + p3;
	p4.print_data();

	p1 = p4 - p2;
	p1.print_data();

	++p1;
	p1.print_data();

	--p1;
	p1.print_data();

	p1++;
	p1.print_data();

	p1--;
	p1.print_data();
}

///
//调用语句 cout << p1 << endl;  
//左操作数 cout //转到定义查看 __PURE_APPDOMAIN_GLOBAL extern _CRTDATA2_IMPORT ostream cout, *_Ptr_cout;
//右操作数 p1
//void operator<<(ostream& out, PluralClass& p) //没有返回值,不支持链式编程
//cout << p << endl;  左移操作符从左向右结合, 
//应该是 cout << p 函数执行之后返回一个返回值 cout 它和 endl 结合执行 cout << endl;函数,实现链式编程。
//可理解为  (cout.operator<<(p)).operator<<(endl);
//所以左移操作符重载函数应返回一个 out 本身
//函数返回值当左值需要返回一个引用
ostream& operator<<(ostream& out, PluralClass& p)
{
	out << p.real << "+" << p.virt << "i";
	return out;
}
//如果用成员函数去实现  cout.operator<<(PluralClass& p); //左操作数.function(右操作数);
//这应该去 ostream 类中去实现,但我们看不到 ostream 类的源码,所以只能用友元函数

//友元函数重载操作符使用场景:<< >> 左移右移操作符  ( << >> 只能用友元函数)
//  =  ()  []  ->  不能用友元函数重载
//一般来说,左右操作数类型不同的时候要用友元函数
//比如上面的 - 号重载 使用了成员函数,
//如果我们     c = p - 4;  //  p.operator-(4); 可以
//若  c = 4 - p; // 4.operator-(p); 错 //4不是类对象,无法调用类的成员函数
void FuncTest2()
{
	PluralClass p1(1, 2);
	//cout << p1; //void operator<<(ostream& out, PluralClass& p)
	cout << p1 << endl; //ostream& operator<<(ostream& out, PluralClass& p)
}

///
class MyClassStr
{
public:
	MyClassStr(const char* str)
	{
		this->len = strlen(str);
		this->str = new char[len + 1];
		strcpy(this->str, str);
	}
	~MyClassStr()
	{
		if (this->str != NULL)
		{
			delete[] this->str;
		}
		this->len = 0;
	}
public:
	void PrintStr()
	{
		cout << this->str << endl;
	}
public:
	//函数返回值当左值需要返回一个引用
	//void operator=(MyClassStr& s) //不支持链式编程   S1 = S2 = S3;
	MyClassStr& operator=(MyClassStr& s) //返回等号左操作数本身,支持链式编程
	{
		if (this->str != NULL)
		{
			delete[] this->str; //防止内存泄漏
			this->len = 0;
		}

		this->len = s.len;
		this->str = new char[this->len + 1];

		strcpy(this->str, s.str);

		return *this;
	}
private:
	int		len;
	char*	str;
public:
	//char operator[](int index); //不支持左值  S1[0] = '1';
	char& operator[](int index) //函数返回值当左值需要返回一个引用
	{
		return this->str[index];
	}
public:
	bool operator==(MyClassStr& s)
	{
		if (this->len != s.len)
		{
			return false;
		}

		if (0 == strcmp(this->str, s.str))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	bool operator!=(MyClassStr& s)
	{
		return !(*this == s);
	}
public:
	void operator()(int a, int b) //可根据业务需要设置参数,返回值
	{
		cout << "hello word!" << endl;
		cout << a << " " << b << endl;
	}
public:
	int operator>(MyClassStr& s) const //this 指针不可修改
	{
		//相减,大于0则返回大于0的数,
		return strcmp(this->str, s.str);
	}
	int operator<(MyClassStr& s)
	{
		return strcmp(s.str, this->str);
	}
	int operator>(const char* p) const
	{
		return strcmp(this->str, p);
	}
	int operator<(const char* p) const
	{
		return strcmp(p, this->str);
	}
public:
	//技巧1,只分配内存的构造函数
	MyClassStr(int len = 0)
	{
		if (len == 0)
		{
			this->len = 0;
			this->str = new char[this->len + 1]; //多分配一个字节 \0 结束符
			strcpy(this->str, "");
		}
		else
		{
			this->len = len;
			this->str = new char[this->len + 1];
			memset(this->str, 0, this->len);
		}
	}
	//技巧2,把类的属性暴露出来,提供外部改变类私有属性的接口
	char* ret_str()
	{
		return this->str;
	}
	int ret_len()
	{
		return this->len;
	}

	friend istream& operator>>(istream& in, MyClassStr& s); //返回引用支持链式编程
};

//重载 = 操作符  //释放原内存//分配新内存//赋值数据//返回本身
void FuncTest3()
{
	MyClassStr S1("abcdefg");
	S1.PrintStr();

	MyClassStr S2("12345");
	S2.PrintStr();

	S2 = S1;
	S2.PrintStr();
}

//char& operator[](int index) //并须在类中,因为友元函数没有this指针
//错误(活动)	E0341	“operator[]”必须是成员函数
//char& operator[](MyClassStr& str, int index)

//重载 [] //函数返回值当左值,必须返回一个引用
void FuncTest4()
{
	MyClassStr S1("qwertyu");

	cout << S1[2] << endl;

	S1[0] = '1';
	S1.PrintStr();
}

//重载 == !=
void FuncTest5()
{
	MyClassStr S1("12345");
	MyClassStr S2("abcde");

	if (S1 == S2)
	{
		cout << "S1 = S2" << endl;
	}
	else
	{
		cout << "S1 != S2" << endl;
	}

	S1 = S2;
	if (S1 != S2)
	{
		cout << "S1 != S2" << endl;
	}
	else
	{
		cout << "S1 = S2" << endl;
	}
}

//其他特殊的重载  () && ||
// && || 内置了短路规则
void FuncTest6()
{
	MyClassStr s1("123");

	s1(1, 3);

	/*
	if ((表达式1) && (表达式2))
	{
		//短路规则
		//如果 表达式1 为假,那么直接执行 语句2 ,表达式2 将不再执行
		语句1;
	}
	else
	{
		语句2;
	}
	*/
	/*
	class t1 = 0;
	class t2 = 1;

	if(t1 && (t1 + t2))
	{
		cout << "ture" << endl;
	}
	//相当于函数调用 t1.operator&&((t1+t2));  t1+t2 是函数参数,会先执行
	// && 是双目运算符,有两个参数,一个是 this 指针,t1+t2 作为实参,应先计算实参表达式
	//在这个函数中,会先执行 operator+() 这个函数调用,再执行 operator&&() 函数
	//得到的结果可能是错误的
	//按照短路规则,t1为0,不应该执行 t1 + t2 这个语句
	//所以  && || 可以进行运算符重载,但是无法实现短路规则
	//一般不对 && || 进行重载

	*/
}

///
//重载大于小于号
void FuncTest7()
{
	MyClassStr s1("aaaaa"), s2("12");

	if (s1 > s2)
	{
		cout << "s1 > s2" << endl;
	}
	else
	{
		cout << "s1 < s2" << endl;
	}

	if (s1 > "a")
	{
		cout << "s1 > \"a\"" << endl;
	}
	else
	{
		cout << "s1 < \"a\"" << endl;
	}
}

///
istream& operator>>(istream& in, MyClassStr& s)
{
	cin >> s.str;
	return in;
}

//类编程技巧
void FuncTest8()
{
	MyClassStr s1(6);

	strcpy(s1.ret_str(), "abcde"); //在类的外部改变类的私有属性
	s1.PrintStr();

	cout << "请输入:";
	cin >> s1;
	s1.PrintStr();
}

int main(void)
{
	FuncTest1();
	FuncTest2();
	FuncTest3();
	FuncTest4();
	FuncTest5();
	FuncTest6();
	FuncTest7();
	FuncTest8();

	system("pause");
	return 0;
}

【四、const与this指针详解】详解C与C++中const的异同,类中的const

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 15:50:34  更:2022-03-03 15:52:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:23:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码