09—C++类和对象—友元和运算符重载

20类—友元(全局函数做友元)

#include<iostream>
using namespace std;
#include<string>
//全局函数做友元
/*
生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,
就需要用到友元的技术

友元的目的就是让一个函数或者类访问另一个类中私有成员

友元的关键字为:friend

友元的三种实现

1.全局函数做友元
2.类做友元
3.成员函数做友元
*/

//创建一个类
class Buliding{
	//这个全局函数可以访问类中的私有成员
	friend void myFriend01(Buliding *building);
	
	//构造函数
public:
	Buliding()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
	//成员函数
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};

//创建全局函数(为了访问类中的私有成员,我们把它声明到相应的类中)
void myFriend01(Buliding *building)
{
	cout << "好朋友(全局函数),要走进:" << building->m_SittingRoom << endl;
	cout << "好朋友(全局函数),要走进:" << building->m_BedRoom << endl;
}

void Test01()
{
	Buliding b1;
	myFriend01(&b1);
}
int main(){
	Test01();
	system("pause");
	return 0;
}

21类—友元(友元类)

#include<iostream>
using namespace std;
#include<string>
//友元类
/*
我们创建两个类
class Bulidding{};
class MyFriend{};
本次我们要在类外写类的构造函数(加上作用域即可),例如Building类
*/

class Building{
	//声明好朋友类,为了能访问Building类中的私有属性
	friend class Myfriend;

	//构造函数(类内声明类外定义)
public:
	Building();

	//成员函数
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};
//类外定义构造函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

class Myfriend{
public:
	Myfriend(){
		//创建一个建筑物对象
		building = new Building;
	}
	Building *building;

	//参观函数,访问Buliding类中的属性
	void visit(){
		cout << "好朋友(类),要走进:" << building->m_SittingRoom << endl;
		cout << "好朋友(类),要走进:" << building->m_BedRoom << endl;
	}

};
void Test01()
{
	Myfriend m1;
	m1.visit();
}
int main(){
	Test01();
	system("pause");
	return 0;
}

22类—友元(成员函数做友元)

#include<iostream>
using namespace std;
#include<string>
//友元类
/*
我们创建两个类
class Bulidding{};
class MyFriend{};
本次我们要在类外写类的构造函数(加上作用域即可),例如Building类
*/

class Building;
class MyFriend{
public:
	//构造函数(类内声明类外定义)
	MyFriend();
	Building *building;

	//参观函数01,可以访问Buliding类中的私有属性
	void visit01();
	//参观函数02,不可以访问Buliding类中的私有属性
	void visit02();
};
class Building{

	friend void  MyFriend::visit01();//声明友元,其他类成员函数可以访问类内的私有属性
public:
	//构造函数(类内声明类外定义)
	Building();
	//成员变量
public:
	string m_SittingRoom;
private:
	string m_BedRoom;
};

//类外定义构造函数
MyFriend::MyFriend(){
	//创建一个建筑物对象
	building = new Building;
}
void MyFriend::visit01(){
	cout << "好朋友(成员函数visit01),要走进:" << building->m_SittingRoom << endl;
	cout << "好朋友(成员函数visit01),要走进:" << building->m_BedRoom << endl;
}
void MyFriend::visit02(){
	cout << "好朋友(成员函数visit02),要走进:" << building->m_SittingRoom << endl;
	//cout << "好朋友(成员函数visit02),要走进:" << building->m_BedRoom << endl;
}

//类外定义构造函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}
void Test01()
{
	MyFriend m1;
	m1.visit01();
	m1.visit02();
}

int main(){
	Test01();
	system("pause");
	return 0;
}

23类—运算符重载(加号运算符重载)

#include<iostream>
using namespace std;
#include<string>
//加号运算符重载
/*
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

对于内置的数据类型,编译器知道怎么去计算
例如:int a=10;	int b=20;	int c=a+b;
对于自定义的数据类型,编译器就不知道如何去计算了
例如:两个类对象相加

加号运算符重载:
作用:实现两个自定义数据类型相加的运算

使用编译器提供的函数名称operator+,最后可以简写为+

成员函数实现加号运算符重载
全局函数实现加号运算符重载
运算符重载 可以发生函数重载

注意:不要乱用运算符重载,对于内置的运算符不要重载,一般是重载自定义类型
*/

class Person{
public:
	//1.成员函数实现加号运算符重载(使用编译器提供的函数名称operator+,最后可以简写为+)
	Person operator+(Person &p)
	{
		Person temp;
		//注意,谁调用这个函数,this代表谁,此处p为传进来的参数
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_A + p.m_B;
		return temp;
	}
	int m_A;
	int m_B;
};

//2.全局函数实现加号运算符重载
Person operator+(Person& p1, Person& p2)
{
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}

//3.运算符重载 可以发生函数重载 
Person operator+(Person& p1, int num)
{
	Person temp;
	temp.m_A = p1.m_A +num;
	temp.m_B = p1.m_B + num;
	return temp;
}
void Test01(){
	Person p1;
	Person p2;
	p1.m_A = 10;
	p1.m_B = 10;
	p2.m_A = 20;
	p2.m_B = 20;

	//实现加号运算符重载

	//1.成员函数方式:Person p3 = p1.operator+(p2);<==>Person p3 = p1 + p2;
	//Person p3 = p1 + p2;
	Person p3 = p1.operator+(p2);

	//2.全局函数方式:Person p3=operator+(p1,p2);<==>Person p3 = p1 + p2;
	Person p4=operator+(p1,p2);
	//Person p3 = p1 + p2;

	//注意:由于以上两种方式的省略写法相同(成员函数方式,全局函数形式)
	//所以当都存在的时候不能使用省略形式,编译器会懵逼!!!!!

	//3.运算符重载 可以发生函数重载 
	//Person p3=operator+(p1,10);<==>Person p3 = p1 + 10;
	Person p5 = p1 + 10;
	cout << "p3.m_A=" << p3.m_A << endl;
	cout << "p3.m_B=" << p3.m_B << endl;

	cout << "p4.m_A=" << p4.m_A << endl;
	cout << "p4.m_B=" << p4.m_B << endl;

	cout << "p5.m_A=" << p5.m_A << endl;
	cout << "p5.m_B=" << p5.m_B << endl;
}
int main(){
	Test01();
	system("pause");
	return 0;
}

24类—运算符重载(左移运算符重载)

#include<iostream>
using namespace std;
#include<string>
/*
左移运算符:<<
左移运算符重载:
作用:可以输出自定义数据类型

一般的左移运算符只能输出内置数据类型(int,float,double等)

总结:重载左移运算符配合友元可以实现输出自定义数据类型

cout属于标准的输出流对象:ostream
*/
class Person
{
	//使用友元,让全局函数可以访问类的私有属性
	friend ostream & operator<<(ostream &cout, Person p);
public:
	Person(int a, int b)
	{
		this->m_A = a;
		this->m_B = b;
	}
	//利用成员函数重载 左移运算符	p.operator<<(cout) 简化版本p<<cout,不是我们想要的
	//通常不适用成员函数重载<<运算符,因为无法实现cout在左侧
	
	//void operator<<(cout){}
private:
	int m_A;
	int m_B;
};
//使用引用接收,使其返回ostream对象本身
ostream & operator<<(ostream &cout, Person p)//本质 operator<<(cout,p)简化cout<<p
{
	cout << "a=" << p.m_A << " b= " << p.m_B;
	return cout;
}
void test() {
	Person p1(10, 20);
	/*
	成员函数的调用使用对象名称加点的方式,例如:p.operator<<(cout)<==>p<<cout
	全局函数的调用方式直接使用函数名称调用即可,例如:operator<<(cout,p)<==>cout<<p

	于是我们可以根据调用方式来区分全局函数和成员函数
	*/
	operator<< (cout, p1) << endl;

	//为了可以在后面继续追加我们需要返回cout本身,因此要使用引用的方式来接收
	//ostream & 
	cout << p1 << "  hello world" << endl; //链式编程
}

int main() {
	test();
	system("pause");
	return 0;
}

25类—运算符重载(递增运算符重载)

#include<iostream>
using namespace std;
#include<string>
/*
递增运算符重载
作用: 通过重载递增运算符,实现自己的整型数据

前置递增:(先加再说,加完再用)
int p=10;
cout<<++p<<endl;	11
cout<<p<<endl;		11
后置递增:(先用再说,用完在加)
int p=10;
cout<<p++<<endl;	10
cout<<p<<endl;		11

总结: 前置递增返回引用,后置递增返回值
*/

//创建我们自己的整形类
class MyInteger {
	//友元
	friend ostream& operator<<(ostream& out, MyInteger myint);
public:
	MyInteger() {
		m_Num = 0;
	}
	//1.前置++
	//成员函数:返回值为MyInteger类型,为了返回对象本身(谁调用这个函数返回谁),使用引用的方式
	MyInteger & operator++() {
		//先++
		m_Num++;
		//再返回
		return *this;
	}

	//2.后置++
	/*
	由于函数重载的条件为函数名称相同,参数列表不同,所以要填加一个占位参数用于区分不同的函数重载
	此处int,为占位参数,可以区分前置递增和后置递增
	*/

	//此处int 为占位参数,可以区分前置递增和后置递增,调用的时候随便传入一个整数即可
	//注意:后置递增返回的是值
	MyInteger operator++(int) {
		//先返回,执行完之后指针被释放
		MyInteger temp = *this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;
		m_Num++;
		return temp;
	}

private:
	int m_Num;
};

//此处重载左移运算符
ostream& operator<<(ostream& out, MyInteger myint) {
	out << myint.m_Num;
	return out;
}

//1.前置++ 先++ 再返回
void test01() {
	MyInteger myInt;
	/*
	调用全局函数:operator<<()
	不简化:operator<<(cout,myInt.operator++());
	第一步简化:cout<< myInt.operator++();
	第二步简化:cout<<++myInt;
	*/

	//myInt.operator++();<==>++myInt;

	cout << ++myInt << endl;
	cout << myInt << endl;
}

//2.后置++ 先返回 再++
void test02() {
	MyInteger myInt;
	/*
	调用全局函数:operator<<()
	不简化:operator<<(cout,myInt.operator++(1));//此处传入的整数1,仅是占位而已
	第一步简化:cout<< myInt.operator++(1);
	第二步简化:cout<<myInt++;
	*/
	//operator<<(cout, myInt.operator++(1)) << endl;

	cout << myInt++ << endl;
	cout << myInt << endl;
}

int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}

26类—运算符重载(赋值运算符)

#include<iostream>
using namespace std;
#include<string>
/*
赋值运算符重载
c++编译器至少给一个类添加4个函数
1. 默认构造函数(无参,函数体为空)
2. 默认析构函数(无参,函数体为空)
3. 默认拷贝构造函数,对属性进行值拷贝
4. 赋值运算符 operator=, 对属性进行值拷贝

注意:赋值运算符会出现深浅拷贝的问题

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
*/

//赋值运算符重载

class Person
{
public:
	Person(int age)
	{
		//将年龄数据开辟到堆区:使用new关键字
		m_Age = new int(age);
	}

	//重载赋值运算符 (使用引用返回自身)
	Person& operator=(Person &p)
	{
		//应该先判断是否有属性在堆区,如果有先释放干净,然后在深拷贝
		//注意:谁拷贝别人就先释放谁,释放完在拷贝,避免内存泄漏
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		//编译器提供的代码是浅拷贝
		//当析构函数释放内存的时候会发生重复释放的问题
		//m_Age = p.m_Age;

		//提供深拷贝 解决浅拷贝的问题
		m_Age = new int(*p.m_Age);

		//返回自身:为了实现连等操作,
		//返回自身还可以给别人再次赋值
		return *this;
	}

	~Person()
	{
		//堆区数据需要程序员手动释放
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
	}

	//年龄的指针
	int *m_Age;

};


void test01()
{
	Person p1(18);
	Person p2(20);
	Person p3(30);
	p3 = p2 = p1; //赋值操作
	cout << "p1的年龄为:" << *p1.m_Age << endl;
	cout << "p2的年龄为:" << *p2.m_Age << endl;
	cout << "p3的年龄为:" << *p3.m_Age << endl;
}

int main() {
	test01();
	//int a = 10;
	//int b = 20;
	//int c = 30;
	//内置数据类型允许这种连等操作
	//c = b = a;
	//cout << "a = " << a << endl;
	//cout << "b = " << b << endl;
	//cout << "c = " << c << endl;
	system("pause");
	return 0;
}

27类—运算符重载(关系运算符)

#include<iostream>
using namespace std;
#include<string>
/*
关系运算符重载:==
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
*/

class Person
{
public:
	//有参构造
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	};
	//重载关系运算符:==
	bool operator==(Person & p)
	{
		//判断是否相等(相等返回true)
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	//重载关系运算符:!=
	bool operator!=(Person & p)
	{
		//判断是否相等(相等返回false)
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return false;
		}
		else
		{
			return true;
		}
	}
	string m_Name;
	int m_Age;
};

void test01()
{
	//内置数据类型
	//int a = 0;
	//int b = 0;

	//自定义数据类型
	Person a("孙悟空", 18);
	Person b("孙悟空", 18);
	//原型:a.operator==(b);
	//简化:a==b;
	if (a == b)
	{
		cout << "a和b相等" << endl;
	}
	else
	{
		cout << "a和b不相等" << endl;
	}
	//原型:a.operator!=(b);
	//简化:a!=b;
	if (a != b)
	{
		cout << "a和b不相等" << endl;
	}
	else
	{
		cout << "a和b相等" << endl;
	}
}
int main() {
	//test01();
	system("pause");
	return 0;
}

28类—运算符重载(函数调用运算符)

	#include<iostream>
	using namespace std;
	#include<string>
	/*
	函数调用运算符 :() 
	也可以重载
	由于重载后使用的方式非常像函数的调用,因此称为仿函数

	仿函数没有固定写法,非常灵活
	*/
	class MyPrint
	{
	public:
		//重载小括号运算符
		void operator()(string text)
		{
			cout << text << endl;
		}
	};
	void test01()
	{
		//重载的()操作符 也称为仿函数
		MyPrint myFunc;
		//未简化:myFunc.operator()("AISMALL");
		//简化:myFunc("AISMALL);
		myFunc("AISMALL");
		//上面的代码看似像一个函数调用,因此被称为仿函数
	}

	class MyAdd
	{
	public:
		MyAdd()
		{
			cout << "调用有参构造" << endl;
		}
		~MyAdd()
		{
			cout << "调用析构" << endl;
		}
		int operator()(int v1, int v2)
		{
			return v1 + v2;
		}
	};

	void test02()
	{
		MyAdd add;
		//未简化:add.operator()(10, 10);
		//简化:add(10,10);
		int ret = add(10, 10);
		cout << "ret = " << ret << endl;

		//匿名对象调用  
		//这种类名加小括号的方式为匿名对象
		//匿名对象特点:调用完就释放
		cout << "匿名对象特点:调用完就释放" << endl;
		cout<<"匿名对象:"<< "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
	}

	int main() {
		test01();
		test02();
		system("pause");
		return 0;
	}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彤彤的小跟班

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值