C++学习笔记(三) 类(二) 友元和运算符重载

一、类特性(二)

1.1 友元

在类中使用friend修饰全局函数、类、成员函数,即可令被修饰对象访问该类中private声明下的内容

#include<iostream>
#include<string>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */

class CMyHome;
class CMyFriend
{
public:
	CMyFriend();
	void visit(void);
	CMyHome * myhome;
private:

};

class CMyHome
{
	/* 使用friend修饰之后,后面修饰的东西就可以访问该类下的私有成员变量了 */
	friend void test_1(void);
	friend class CMyFriend; /* 其他类做友元 */
public:
	CMyHome();
	string my_livingroom;

private:
	string my_bedroom;
};

CMyFriend :: CMyFriend()
{
	myhome = new CMyHome;
}

void CMyFriend :: visit(void)
{
	cout << "friend正在参观" << myhome->my_livingroom <<endl;
	cout << "然后friend又去参观" << myhome->my_bedroom <<endl; /* 这里要访问类的私有成员变量,就需要友元 */
}

CMyHome :: CMyHome()
{
 	my_livingroom = "livingroom";
	my_bedroom = "bedroom";
}

/* 测试全局函数做友元 */
void test_1(void)
{
	CMyHome mh_1;
	cout << "全局函数访问私有成员变量:" << mh_1.my_bedroom << endl;
}

/* 测试类做友元 */
void test_2(void) 
{
	CMyFriend mf_1;
	mf_1.visit();
}

int main(void)
{
	test_1();
	test_2();
	return 0;
}

2.2 运算符重载

使用operator关键字实现运算符重载,并且运算符重载函数可以有多个定义,只要传入的形参类型不同就可以。

2.2.1 加(减乘除)法重载

#include<iostream>
#include<string>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */

class PersonInner
{
public:
	PersonInner()
	{
		age = 10; 
	}

	PersonInner operator+ (PersonInner p1) /* 运算符重载*/
	{
		PersonInner ptemp;
		ptemp.age = this->age + p1.age;
		return ptemp;
	}
	int age;
};

class Person
{
public:
	Person()
	{
		age = 10; 
	}
	int age;
};


Person operator+ (Person p1, Person p2)
{
	Person ptemp;
	ptemp.age = p1.age + p2.age;
	return ptemp;
}

Person operator+ (Person p1,int age_in)
{
	Person ptemp;
	ptemp.age = p1.age + age_in;
	return ptemp;
}

/* 测试类中成员函数实现运算符重载 */
void test_1(void)
{
	PersonInner p1,p2;
	PersonInner p3 = p1 + p2;
	cout << "使用类中成员函数实现运算符重载时,p3的age: " << p3.age << endl; /* 20 */
}

/* 测试全局函数实现运算符重载 */
void test_2(void) 
{
	Person p1,p2;
	Person p3 = p1 + p2;
	Person p4 = p3 + 100;
	cout << "使用全局函数实现运算符重载时,p3的age: " << p3.age << endl; /* 20 */
	cout << "使用全局函数实现运算符重载时,p4的age: " << p4.age << endl; /* 120 */
}

int main(void)
{
	test_1();
	test_2();
	return 0;
}

2.2.2 左移运算符重载

这个应该不常用,最好也别用,反而会越来越乱

#include<iostream>
#include<string>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */

class Person
{
public:
	Person()
	{
		age_1 = 10; 
		age_2 = 20;
	}

	int age_1;
	int age_2;
};

/* 只能利用全局函数重载左移运算 
并且因为cout全局只能有一个,所以不能创建新的,只能用引用的方式 */
void operator<< (ostream & cout,Person p)
{
	cout << p.age_1 << " " << p.age_2 << endl;
}

/* 测试<<运算符重载 */
void test_1(void)
{
	Person p1;
	cout << "使用全局函数实现<<符重载,打印p成员变量: "  << endl;
	cout << p1;
}

int main(void)
{
	test_1();
	return 0;
}

2.2.3 赋值运算符重载

举个例子,赋值运算符重载在类对象拷贝中的用法:C++编译器会给一个类添加四个默认函数:默认构造函数、默认析构函数、默认拷贝构造函数、默认赋值运算符=重载函数(与默认拷贝构造函数作用相同,对属性进行值拷贝)。但是默认的赋值运算符重载函数只是进行了浅拷贝,实例化对象的指针成员变量在经过赋值操作后会指向同一个变量(同一块内存)。这样可能出现两个问题:

  1. 某个实例化对象通过该指针对该变量进行了修改,将导致所有实例化对象在解引用的时候的值发生变化。
  2. 如果在初始化指针的时候使用了new来开辟内存块,可能会不经意间导致在delete时出现错误

第二个问题是因为一般类中new开辟的内存块会重写析构函数,在构函数中释放该内存块,当有多个实例化对象并进行浅拷贝后可能会对该堆区内存进行重释放。当然如果能保证所有的实例化对象只对该指针指向的内存块进行一次释放也不是不行,但是没必要。

此外,在尝试使用全局函数来重载=运算符时出错,看到这篇博客后发现原因是c++规定有些运算符只能使用类内成员函数重载,

#include<iostream>
#include<string>
using namespace std; /* 这一句到底什么作用?不加这句就会显示cout报错 */

class CPerson
{
public:
	CPerson(int age)
	{
		person_age = new int(age); 
	}

	int *person_age;
};

class CPeople
{
public:

	CPeople()
	{
		people_age = new int(0); 
	}

	CPeople(int age)
	{
		people_age = new int(age); 
	}
	
	CPeople& operator= (CPeople &people_temp)
	{
		if(people_age != NULL)
		{
			delete people_age;
			people_age = NULL;
		}
		people_age = new int(*people_temp.people_age);
		return *this;
	}

	~CPeople()
	{
		if(people_age != NULL)
		{
			delete people_age;
			people_age = NULL;
		}
	}

	int *people_age;
};


/* 对于堆区数据,只是进行了浅拷贝,即p1.person_age和p2.person_age都指向了同一个内存
	并且如果在析构函数中delete开辟的堆内存,就会报错,因为重复delete了同一块内存 */
void test_1(void)
{
	CPerson p1(10);
	CPerson p2 = p1;
	cout << "person1和person2的*person_age分别为: p1:"  << *p1.person_age << " p2:" << *p2.person_age <<endl;
	*p2.person_age = 20;
	cout << "现在person1和person2的*person_age分别为: p1:"  << *p1.person_age << " p2:" << *p2.person_age <<endl;
}

/* 如果使用=重载函数进行深拷贝 */
void test_2(void)
{
	CPeople p1(10);
	CPeople p2(20);
	// CPeople p2 = p1; //这样会出错,还是会经历test_1中的情况,也就是说这行代码不走自己定义的重载函数,但是我已经加了无参的构造函数了啊,奇怪
	p2 = p1;
	cout << "people1和people2的*people_age分别为: p1:"  << *p1.people_age << " p2:" << *p2.people_age <<endl;
	*p2.people_age = 20;
	cout << "现在people1和people2的*people_age分别为: p1:"  << *p1.people_age << " p2:" << *p2.people_age <<endl;
}

int main(void)
{
	test_1();
	test_2();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值