C++学习笔记7


前言

学习C++的第n天


一、友元

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

友元的关键字:friend

友元的三种实现

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元

1.全局函数做友元

friend 全局函数

代码如下(示例):

class build
{
	//告诉编译器 goodgay是全局函数 是 build类的好朋友,可以访问类中的私有内容
	friend void goodgay(build* building);
public:
	build()	//初始化
	{
		this->m_redroom = "卧室";
		this->m_sittingroom = "客厅";
	}
public:
	string m_sittingroom; //客厅
private:
	string m_redroom;	//卧室
};

void goodgay(build* building)
{
	cout << "好基友正在访问:" << building->m_sittingroom << endl;
	cout << "好基友正在访问:" << building->m_redroom << endl;
}
void test01()
{
	build b;
	goodgay(&b);
}

2. 类做友元

friend 类

代码如下(示例):

#include<iostream>
using namespace std;

class build;
class goodgay
{
public:
	goodgay();	//构造函数
	void visit();

private:
	build* building;	//创建指针
};

class build
{
	//告诉编译器 goodgay 是 build类的好朋友,可以访问类中的私有内容
	friend class goodgay;
public:
	build();	//可以在类内声明,在类外定义函数
	
public:
	string m_sittingroom; //客厅
private:
	string m_redroom;	//卧室
};

build::build()	
{
	this->m_redroom = "卧室";
	this->m_sittingroom = "客厅";
}
goodgay::goodgay()
{
	building = new build;	//在堆区创建一个类,并用指针指向它
}
void goodgay::visit()	//用goodgay类中的指针访问build类中的私有成员
{
	cout << "好基友正在访问:" << building->m_sittingroom << endl;
	cout << "好基友正在访问:" << building->m_redroom << endl;
}

void test01()
{
	goodgay b;
	b.visit();
}
int main()
{
	test01();
	return 0;
}

3. 成员函数做友元

friend void goodgay::visit();

代码如下(示例):

#include<iostream>
using namespace std;

class build;
class goodgay
{
public:
	goodgay();
	
	void visit();	//让visit函数可以访问build中私有成员 
	void visit2();	//让visit2函数不可以访问build中私有成员

	build* building;	//创建指向build类的指针
};
class build
{
	//告诉编译器 goodgay类下的visit函数作为本类的好朋友,可以访问私有成员
	friend void goodgay::visit();
public:
	build();	//可以在类内声明,在类外定义函数

public:
	string m_sittingroom; //客厅
private:
	string m_redroom;	//卧室

};
//类外实现成员函数
build::build()
{
	this->m_redroom = "卧室";
	this->m_sittingroom = "客厅";
}
goodgay::goodgay()
{
	building = new build;
}
void goodgay::visit()
{
	cout << "visit函数正在访问:" << building->m_sittingroom << endl;
	cout << "visit函数正在访问:" << building->m_redroom << endl;
}
void goodgay::visit2()
{
	cout << "visit2函数正在访问:" << building->m_sittingroom << endl;
	//cout << "visit函数正在访问:" << building->m_redroom << endl;这个会报错,因为不是build类的好朋友
}
void test01()
{
	goodgay gg;
	gg.visit();
}
int main()
{
	test01();
	return 0;
}

二、C++运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

1. 加号运算符重载

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

  1. .成员函数重载+号
    person operator+ (形参)
  2. 全局函数重载+号
    person operator+(形参1, 形参2);

代码如下(示例):

#include<iostream>
using namespace std;

class person
{
public:
	//1.成员函数重载+号
	/*person operator+ (person& p)
	{
		person temp;
		temp.m_a = this->m_a + p.m_a;
		temp.m_b = this->m_b + 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;
}
//函数重载的版本
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;
	p1.m_a = 10;
	p1.m_b = 10;
	person p2;
	p2.m_a = 10;
	p2.m_b = 10;
	//1.成员函数重载本质调用
	//person p3 = p1.operator+(p2);
	//2.全局函数重载本质调用
	//person p3 = operator(p1,p2);
	person p3 = p1 + p2;
	cout << p3.m_a << endl;
	cout << p3.m_b << endl;

	//运算符重载 也可以发生函数重载
	person p4 = p1 + 100; // person + int
	cout << p4.m_a << endl;
	cout << p4.m_b << endl;
}

int main()
{
	test01();
	return 0;
}

总结1:对于内置的数据类型的表达式的运算符是不可能改变的
总结2:不要滥用运算符重载

2.左移运算符重载

作用:可以输出自定义数据类型
全局函数 ostream& operator<<(ostream& out, person& p)

cout<<p1;
我们可以定义输出

代码如下(示例):

#include<iostream>
using namespace std;

class person
{
	friend ostream& operator<<(ostream& out, person& p);
public:

	person(int a, int b)
	{
		this->m_a = a;
		this->m_b = b;	
	}
	//成员函数 实现不了 p<<cout 不是我们想要的效果
	//void operator<<(person &p){}

private:
	int m_a;
	int m_b;
};
//全局函数实现左移重载
//ostream对象只能有一个
ostream& operator<<(ostream& out, person& p)	//out是别名 ostream:输出流,是一个类型
{
	out << "a:" << p.m_a << "b:" << p.m_b;
	return out;
}

void test()
{
	person p1(10, 20);
	cout << p1 << "hello world" << endl; //链式编程
}
int main()
{
	test();
	return 0;
}

3. 递增运算符重载

作用:通过重载递增运算符,实现自己的整型数据

代码如下(示例):

#include<iostream>
using namespace std;

class myinteger {
	friend ostream& operator<<(ostream& out, myinteger myint);
public:
	myinteger()
	{
		m_num = 0;
	}
	//前置++
	myinteger& operator++()
	{
		//先++
		m_num++;
		//再返回
		return *this;
	}
	//后置++
	myinteger operator++ (int)	//占位函数
	{
		//先返回
		myinteger temp = *this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++
		m_num++;
		return temp;
	}
private:
	int m_num;
};

//前置++ 先++,后返回
void test01()
{
	myinteger myint;
	cout << ++myint << endl;
	cout << myint << endl;
}
//后置++ 先返回 再++
void test02()
{
	myinteger myint;
	cout << myint++ << endl;
	cout << myint << endl;
}
ostream& operator<<(ostream& out, myinteger myint)
{
	out << myint.m_num;
	return out;
}
int main()
{
	test01();
	//test02();
	system("pause");
	return 0;
}

4. 赋值运算符重载

C++编译器至少给一个类添加4个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝
  4. 赋值运算符 operator= , 对属性进行值拷贝

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

代码如下(示例):

#include<iostream>
using namespace std;

class person
{
public:

	person(int age)
	{
		//将年龄数据开辟到堆区
		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 << *p2.m_age << endl;
	cout << *p3.m_age << endl;
}
//浅拷贝会报错,因为重复释放同一块内存
int main()
{
	test01();
	//test02();
	system("pause");
	return 0;
}

总结:要将对象1的值赋值给对象2,若对象1中的值创建在堆区,需要进行赋值运算符重载,
先判断对象2的属性是否为空,清理后,再深拷贝

5.关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
bool operator==(引用形参)
bool operator!=(引用形参)
代码如下(示例):

#include<iostream>
using namespace std;

class person
{
public:
	person(string name, int age)
	{
		this->m_age = age;
		this->m_name = name;
	};
	bool operator==(person& p)
	{
		if (this->m_age == p.m_age && this->m_name == p.m_name)
		{
			return true;
		}
		else {
			return false;
		}
	}
	bool operator !=(person& p)
	{
		if (this->m_age == p.m_age && this->m_name == p.m_name)
		{
			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);
	if (a == b)
	{
		cout << "a和b相等" << endl;
	}
	else {
		cout << "a和b不相等" << endl;
	}
	if (a != b)
	{
		cout << "a和b不相等" << endl;
	}
	else {
		cout << "a和b相等" << endl;
	}
}
int main()
{
	test01();
	//test02();
	system("pause");
	return 0;
}

6.函数调用运算符重载

  • 函数调用运算符()也可以重载
  • 由于重载后使用的方式非常像函数的调用,因此称为仿函数
  • 仿函数没有固定写法,非常灵活
    void operator()(形参)

代码如下(示例):

#include<iostream>
using namespace std;
#include<string>

class myprint
{
public:
	void operator()(string text)
	{
		cout << text << endl;
	}
};
void test01()
{
	//重载的()操作符 也称为仿函数
	myprint myfunc;
	myfunc("hello world");
}
class myadd
{
public:
	int operator()(int v1, int v2)
	{
		return v1 + v2;
	}
};
void test02()
{
	myadd add;
	int ret = add(10, 10);
	cout << "ret = " << ret << endl;

	//匿名对象调用
	cout << "myadd()(100,100) = " << myadd()(100, 100) << endl;	//使用完后就释放了
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页