C++类的详解----从计算机内存机制开始讲起,适合零基础非计科专业

                                              **C++类的使用**
  • 写在最前面:一般来讲,我们创建一个类class的时候,是需要重新建一个头文件,以.h形式的文件来写类的声明,内联函数,甚至可以在该函数中实现类的成员函数。
  • 当然,我们一般不在.h文件中实现成员函数,我们会在源文件中再建一个.cpp文件,用于实现类。
  • 我们之所以不直接在main文件中写class,是为了防止该文件太臃肿。并不是说不能写。

一、对象的动态创建与释放

-------关于类的了解,还需要从内存四区开始讲起的。在C++指针那块,我已经说了,学C++,首先要学习内存,了解内存。
在这里插入图片描述
------在C语言中,只有堆区是程序员可以自己操作的,程序员自己分配,动态创建内存。
------在原始的C语言中,我们程序员使用malloc()申请创建内存空间(也可以说创建变量),用free()释放内存空间。但是在C中,malloc()和free()是函数。不是C语言语法的部分,是一个库函数。
------在C++中,我们使用的是new和delete,他们两个是C++的运算符,不是函数,所以运行效率更高一点。
------但是这两组的作用是一样的。他们的作用分别如下:

1.1 分配基础数据类型

大家直接看代码

void test_base()//测试函数
{
	//C     使用malloc和free
	//分配空间
	int *p_b = (int*)malloc(sizeof(int) / sizeof(char));
	//释放空间
	free(p_b);
	//C++    使用new和 delete
	//分配空间  new 数据类型
	int * p_int = new int;//分配一个整型数据
	*p_int = 10;
	cout << *p_int <<endl;
	//释放空间
	delete p_int;
}
//主函数
int main()
{
	test_base();
	return 0;
}

这边还需要提一下,new在分配空间的时候可以对空间进行初始化,但是malloc是不行的。
void test_base_init()
{
	int *p_init = new int(20);//分配一个整型空间,并将其直接赋值为20
	cout << *p_init << endl;
	delete p_init;
}
//主函数
int main()
{	test_base_init();
	return 0;
}

1.2 分配数组

------这一部分就有点难度了,需要对指针具有更深一步的掌握。
------该部分如果看不懂的,建议先去看看B站黑马老师关于指针的视频。


//2.   分配数组
void test_arr()
{
	//使用malloc和free
	int *p_arr_mf = (int *)malloc(sizeof(int) / sizeof(char)*10);//分配具有十个空间的整形数组
	free(p_arr_mf);
	//使用new和delete  格式: new 数据类型[个数]
	//分配new数组的时候不能进行初始化
	int *p_arr_nd = new int[10];//分配空间
	//使用for循环赋值
	for (int i = 0; i < 10; i++)
	{
		p_arr_nd[i] = i;
	}
	//打印出来
	for (int j = 0; j < 10; j++)
	{
		cout << p_arr_nd[j] << " ";
	}
	cout<<endl;
	//释放数组,在释放数组的时候一定要加上方框.不加方框会在城内存泄漏,因为,他可能只会释放一个,剩下九个没释放。
	delete[] p_arr_nd;
}
int main()
{
	//test_base();
	//test_base_init();
	test_arr();
	return 0;
}

1.3 分配二维数组

  • 下不多说,直接上代码。看了代码之后再解释。
//分配二维数组int a[5][6]
//分配二维数组
void test_2Darr()
{
	//使用malloc和free
	//1.  先分配一维数组
	int **p_2d_mf = (int **)malloc(sizeof(int) / sizeof(char)*5);
	//2.  给每个成员分配空间
	for (int i = 0; i < 5; i++)
	{
		p_2d_mf[i] = (int*)malloc(sizeof(int) / sizeof(char) * 6);
	}
	//给每个元素赋值
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 6; j++)
		{
			p_2d_mf[i][j] = i * 6 + j;
		}
	}
	//访问每个元素
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 6; j++)
		{
			cout << p_2d_mf[i][j] <<" ";
		}
		cout << endl;
	}
	cout << endl;
	//释放内存   先释放行,在释放列
	for (int k = 0; k < 5; k++)
	{
		free((void*)p_2d_mf[k]);
	}
free((void*)p_2d_mf);
		cout<<"内存已释放"<<endl;

	//使用new  delete分配和释放内存
	//格式    new  数据类型[个数]
	int **p_2d_nd = new int*[5];//关于指针类型,黑马老师
	for (int i = 0; i < 5; i++)
	{
		p_2d_nd[i] = new int[6];
	}
	//释放内存
	for (int i = 0; i < 5; i++)
	{
		delete[] p_2d_nd[i];
	}
delete[] p_2d_nd;
}

在这里插入图片描述
------看图2,对二维数组进行分配内存,需要先分配左边的内存空间,然后再分配右边的内存空间。这也就要要求我们了解左右两边的类型。
------首先,右边是5个一维数组,因此这个指针的类型是int型的,而左边是指向右边一维数组的类型,因此这个指针是int ∗ _{}^{*}\textrm{} 类型(一个指向指针博客的连接),最后可以得到,指向int*的指针的类型是int**。

1.4 分配三维及以上数组

------关于三维数组的讲解,我们就只是用new 和delete方法了。毕竟malloc和free是C的方法。

//三维数组的操作

void test_3darr()

{
	int ***p_3d_nd = new int**[5];//关于指针类型,黑马老师
	for (int i = 0; i < 5; i++)
	{
		p_3d_nd[i] = new int*[6];
	}
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 6; j++)
		{
			p_3d_nd[i][j] = new int[7];
		}
	}
	//释放,反过来释放。
	//先释放
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 6; j++)
		{
			delete[] p_3d_nd[i][j];
		}
	}
	//再释放
	for (int i = 0; i < 5; i++)
	{
		delete[] p_3d_nd[i] ;
	}
	//最后释放
	delete[] p_3d_nd;
}


int main()
{
	//test_base();
	//test_base_init();
	//test_arr();
	//test_2Darr();
	test_3darr();
	return 0;

}

下面我就都不讲malloc和free方法了,因为这个已经过时了,而且没有new和delete方便。

二、动态对象分配

New分配对象的时候,会自动调用构造函数对对象进行构造。

同样的,调用delete是,会自动调用析构函数对对象进行析构

该部分就不举例子了。

三、 静态成员变量

3.1 初始静态成员变量

class Student
{
public:
	Student();//默认构造函数
	~Student();//析构函数
	static int num;//学生人数,静态成员变量
    //静态成员变量不是对象的属性,
	//是类的属性,是所有变量所共享的属性。举例子,在main中创建三个变量
	//静态成员变量必须在类的外部进行初始化,即如果我相对num进行赋值,我必须在类的外面进行赋值
	//静态成员变量是存在数据区(内存四区)的,看图
private:
	int _id;//学生学号
};
Student::Student()
{
}
Student::~Student()
{
}
//对静态成员变量进行初始化(赋值)
int Student::num = 10;
int main()
{
	Student s1,s2, s3;
	return 0;
}

在这里插入图片描述

3.2 静态成员变量的使用

- 1. 通过对象使用
该部分的代码上结上部分的代码

int main()
{
	Student s1,s2, s3;
	//静态成员变量的使用
	//1. 通过对象使用
	cout <<"通过对象s1使用静态成员变量num"<< s1.num << endl;
	s2.num = 25;//修改num的值
	cout <<"通过对象s3使用静态成员变量num"<< s3.num << endl;
	return 0;

}

  • 在上述代码中我们可以看出,我们首先使用对象s1调用num的值,然后通锅s2修改num的值,但是这个时候,仍然能通过s3继续调用num的值。
  • 因此可以说明,这个num是被共享的。
  • 2. 通过类名使用
int main()
{
	Student s1,s2, s3;
	//静态成员变量的使用
	//1. 通过对象使用
	cout <<"通过对象s1使用静态成员变量num = "<< s1.num << endl;
	s2.num = 25;//修改num的值
	cout <<"通过对象s3使用静态成员变量num = "<< s3.num << endl;
	//2. 通过类名来使用
	s3.num = 52;
	cout << "通过类名调用静态成员变量num = " << Student::num << endl;
	return 0;

}

四、 静态成员函数

特点:静态成员函数只能使用静态成员变量。

格式:static void 函数名()
在上述3.1小节下代码框架的public:下写如下代码:

static void showinfo()//显示学生信息
	{
		cout << "显示学生人数" << num << endl;//此时,num是静态成员函数
		//当我们想显示学士id的时候会怎么样呢?
		//cout << "显示学生id" << _id << endl;//此时,_id不是静态成员函数,当大家把注释符去掉后会发现_id报错

	}

静态成员函数的使用和静态成员变量的使用是一样的。

int main()
{
	Student s1,s2, s3;
	//静态成员变量的使用
	//1. 通过对象使用
	cout <<"通过对象s1使用静态成员变量num = "<< s1.num << endl;
	s2.num = 25;//修改num的值
	cout <<"通过对象s3使用静态成员变量num = "<< s3.num << endl;
	//2. 通过类名来使用
	s3.num = 52;
	cout << "通过类名调用静态成员变量num = " << Student::num << endl;
	//静态成员函数的使用
	//可以是用
	s1.showinfo();
	//也可以使用
	Student::showinfo();
	return 0;
}

五、对象的内存模型

//对象的内存模型
class test_model
{
public:
	test_model();//默认构造函数
	~test_model();//析构函数
	//写一个带参构造函数
	test_model(int a,int b)
	{
		int _a = a;
		int _b = b;
	}
	void showinfo()//普通函数可以是用静态成员变量
	{
		cout << "_a = " << _a << endl;
		cout << "_b = " << _b << endl;
		cout << "_c = " << _c << endl;
	}
private:
	int _a;
	int _b;
	//静态成员变量存在数据区,不占内存。
	static int	_c;//需要养成一个好习惯,定义了静态成员变量,就在后面备注,需要在class外初始化变量
};
int test_model::_c = 10;//再class外面给静态成员变量赋值

test_model::test_model()
{
}
test_model::~test_model()
{
}
int main()
{
	//我们上面写了一句话:静态成员变量存在数据区,不占内存。
	//我为什么要吃饱了撑的写这句话呢
	//让我们先来看看

	//测试当前代码大小
	//大家在没运行之前先猜猜代码的大小,提示:一共有三个int型变量,每个int型变量4B,那加起来是多少?
	cout << "代码大小 = " << sizeof(test_model) << endl;
	return 0;
}

在这里插入图片描述

  • 运行完我们发现,明明有3个int型变量的代码,为什么才占据8B的内存呢?
  • 其原因是:静态成员变量不占用对象内存,保存在数据区;而这些函数是存在代码区的,也同样不占内存。

5.1 普通成员函数

  • 下面比较重要了,牵扯到为什么可以直接使用: 类名.变量,类名::变量等方法的直接原因。 首先先看这张图,
    在这里插入图片描述
    ------在上述代码中,t1 和 t2是类test_model的两个对象。很容易的,我们还可以知道,对于函数showinfo(),以及其中的变量a b。这两个对象t1 和 t2,不管谁调用(t2.showinfo()或t1.showinfo())这个函数,那么这个a和b就属于谁,这一点,我们在main()函数中的两个调用代码就可以看出来。调用的时候,不管是获取a和b的值,还是给他们俩赋值,都是可以的。
    ------但是,这个时候我们需要思考一个问题:我们想对其空间内存进行操作,就必须要知道他的地址。例如:如果我们想改变变量a的值,那么我们就必须要知道a的地址。但是,C++是如何做到这一点的呢?
    ------还是以showinfo()为例,该函数想获取或者改变a或者b的值,该函数内部就必须有一个机制来或者相应变量的地址。还有另一个问题就是,showinfo()是怎么知道该是t1还是改用t2的。
  • 现在的关键问题来了:我们是如何获取地址的?
  1. 就showinfo()函数而言,其实他不是一个没有形参的函数,而是内部含有一个指向对象t1和t2地址的指针。
  2. 然后,类的内部实现其实是一个结构体(struct)。但是结构体里面没有函数,只有变量。因此,结构体中是这个类里的所有数据。
  3. 对于普通成员函数,编译器会在其内部添加一个指向当前对象的指针。例如
    上述例子类中的函数为:
test_model(int a,int b)
	{
		int _a = a;
		int _b = b;
	}
但其实,该函数的本质是这样的:
struct test
{//普通成员变量存放在结构体中,而静态成员变量则认为是全局变量,需要在结构体制外定义
	int _a;
	int _b;
};
//初始化静态成员变量c,静态成员变量则认为是全局变量
int _c = 10;
//上述例子类中的函数为:
test_model(int a, int b)
{
	int _a = a;
	int _b = b;
}
//然而其本质是这样的
test_model(test*const p, int a, int b)
{//这个p是指向当前对象的,则这个_a是p的a,因此还可以写成下面一个函数的形式
	int _a = a;
	int _b = b;
}

test_model(test*const p, int a, int b)
{//这个p是指向当前对象的,则这个_a是p的a,因此还可以写成下面一个函数的形式
	p-> _a = a;
	p->_b = b;
}

  • 因为这个时候,这个p是指向当前对象的,所以,变异可以很方便直接的就找到了变量属于哪个对象。

  • 所以,针对上面主函数main()中的一个操作:

//所以有:

test_model t1(1, 2);//这行代码的其实形式,其实是下面的样子
test_model1(&t1, 1,2);
test_model t2(3, 4);
test_model1(&t2, 3, 4);
这个时候,我们再来看一下函数showinfo()
	void showinfo()//普通函数可以是用静态成员变量
	{
		cout << "_a = " << _a << endl;
		cout << "_b = " << _b << endl;
		cout << "_c = " << _c << endl;
	}

------在类class_model中,showinfo()是一个普通成员函数,我们在之前还说过,静态成员函数只能调用静态成员变量,而普通成员函数可以调用所有变量。

void test_showinfo(test* const p)//普通函数可以是用静态成员变量
{
	cout << "_a = " << _a << endl;
	cout << "_b = " << _b << endl;
	cout << "_c = " << _c << endl;
}

//所以main函数中的
t1.showinfo();
t2.showinfo();
//其实是如下便是方式:
test_showinfo(&t1);
test_showinfo(&t2);

------从上面可以看出来,为什么不同对象在被调用的时候,为什么他们的不参数不会被搞乱。
------这也从一个方面说明了,类的普通成员函数,内部都有一个隐藏的指向当前对象的指针,而这个指针,就是this指针。
------也就是说,上面书写showinfo函数的时候,可以这样,

void test_showinfo(test* const p)//普通函数可以是用静态成员变量
{
	cout << "_a = " << this-> _a << endl;
	cout << "_b = " << this->_b << endl;
	cout << "_c = " << this->_c << endl;
}

5.2 静态成员函数

  • 定义方式:static void showinfo_c()
  • 再说一次,静态成员函数,只能使用静态成员变量。
  • 我们在class test_model中定义一个static void showinfo_c(),可以发现:

#include <iostream>
using namespace std;

//对象的内存模型

class test_model
{
public:
	test_model();//默认构造函数
	~test_model();//析构函数
	//写一个带参构造函数
	test_model(int a,int b)
	{
		int _a = a;
		int _b = b;
	}
	void showinfo()//普通函数可以是用静态成员变量
	{
		cout << "_a = " << _a << endl;
		cout << "_b = " << _b << endl;
		cout << "_c = " << _c << endl;
	}
	//定义一个静态成员函数
	static void showinfo_c()//静态成员函数
	{//这个时候我们会发现,this指针报错
		cout << "_a = " << this->_a << endl;
		cout << "_b = " << this->_b << endl;
		cout << "_c = " << this->_c << endl;
	}
private:
	int _a;
	int _b;
	//静态成员变量存在数据区,不占内存。
	static int	_c;//需要养成一个好习惯,定义了静态成员变量,就在后面备注,需要在class外初始化变量
};
int test_model::_c = 10;//再class外面给静态成员变量赋值
test_model::test_model()
{
}
test_model::~test_model()
{
}

  • 为什么静态成员函数智能调用静态成员变量呢?
  • 原因在于静态成员函数内部没有this指针,因此没有办法使用。
  • 也可以说:静态成员函数实现的时候没有为其添加指向当前对象的指针。
  • 但是为什么可以用静态成员变量呢?
  • 因为静态成员变量是全局的

六、 友元函数和友元类

6.1 友元函数

先看下面的代码


//继续使用学生类
//我们想写一个信息显示函数,showinfo(),我们有两种方式,
//1. 内部方式,在类内部实现
//2. 外部方式,
//3. 友元函数
//友元函数:如果一个函数是某个类A的友元函数,那么这个函数可以访问这个类A中的所有成员(包括私有成员)。
class Student
{
public:
	Student();//默认构造函数
	~Student();//析构函数
	//带参构造
	Student(int id =0, char* name = NULL)
	{
		this->_id = id;
		this->_name = name;
	}
	//内部函数
	void showinfo_n()
	{
		cout << "学生学号:" << _id << endl;
		cout << "学生姓名:" << _name << endl;
	}
private:
	int _id;
	char *_name;
};
//外部函数.需要多定义一个参数
void showinfo_w(Student &s)
{
	//私有成员变量在类的外部是不能访问的,因此报错了。
	//所以需要写一个友元函数
	cout << "学生学号:" << s._id << endl;
	cout << "学生姓名:" << s._name << endl;
}
Student::Student()
{
}
Student::~Student()
{
}
int main()
{
	Student s;
	showinfo_w(s);
}

这个时候我么你发现,如果在类外部写一个show函数,会出现很多问题,所以我们需要写一个友元函数。

友元函数:如果一个函数是某个类A的友元函数,那么这个函数可以访问这个类A中的所有成员(包括私有成员)。
友元函数的定义方法:

  1. 在类中写函数的声明;
  2. 在函数声明前加 friend。
class Student
{
	//定义Student的友元函数
	friend void showinfo_y(Student &s);//声明友元函数,y是友元的意思
public:
	
	//带参构造
	Student(int id=1, const char *name = NULL)
	{
		this->_id = id;
		this->_name = name;
	}
	//内部函数
	void showinfo_n()
	{
		cout << "学生学号:" << _id << endl;
		cout << "学生姓名:" << _name << endl;
	}

private:
	int _id;
	const char *_name;
};

//在类外部实现该友元函数
void showinfo_y(Student &s)//声明友元函数
{
	cout << "学生学号:" << s._id << endl;
	cout << "学生姓名:" << s._name << endl;
}

int main()
{
	Student s(89,"王大锤");
	showinfo_y(s);
}

这个时候,我们调用该函数就不会出错了。

注意点:

  1. 友元函数是类的外部函数,因此友元函数中没有this指针。
  2. 友元函数破坏类的封装性,尽量不要用。
  3. 友元函数的声明可以放到类的任何位置(必须放到类中,不能放在类外面),不受访问控制符的影响。解释一下:
    在这里插入图片描述
    我们在声明这个友元函数的时候,并没有将其写在,public中,也没有写在private中,也没有写在protected中。其实,这个友元函数可以写在任何地方。
    顺便再提一下:
    public:
    private:
    protected:
    这三个就是我们所说的访问控制符。这个不难理解,

6.2 友元类 friend class

如果一个类B是另一个类A的友元类,那么类B中所有函数都是类A的友元函数。

比如,对于上一个Student类来讲,该类中包含了学生的学号和姓名信息,但是加入我们还需要一个类来存储学生的家庭地址。我们就需要重新写一个地址类B。

格式是:
Class A
{

Private:

Public:

}

Class B
{
Friend class A;//加上这一句式关键。
Private:

Public:

}
大家直接看代码


class Address;//声明一个类
class Student
{
	//定义Student的友元函数
	//friend void showinfo_y(Student &s);//声明友元函数,y是友元的意思
public:
	
	//带参构造
	Student(int id=1, const char *name = NULL)
	{
		this->_id = id;
		this->_name = name;
	}
	//内部函数
	void showinfo_n(Address &add);//声明该函数
private:
	int _id;
	const char *_name;
};
//定义一个友元类
class Address
{
	friend class Student;
public:
	Address(const char *sheng, const char *shi, const char *qv, const char *jiedao)//声明一下一个带参构造函数
	{
		_sheng = sheng;
		_shi = shi;
		_qv = qv;
		_jiedao = jiedao;
	}
private:
	const char *_sheng;
	const char *_shi;
	const char *_qv;
	const char *_jiedao;
};
//外部实现showinfo_n
void Student::showinfo_n(Address &add)
{
	cout << "学生学号:" << _id << endl;
	cout << "学生姓名:" << _name << endl;
	cout << "省:" << add._sheng << endl;
	cout << "市:" << add._shi << endl;
	cout << "区:" << add._qv << endl;
	cout << "街道:" << add._jiedao << endl;
}
int main()
{
	Student s(89,"王大锤");
	Address add("江苏", "徐州", "新沂", "小湖街道");
		//showinfo_y(s);
	s.showinfo_n(add);
}

仔细看一下上面这段代码,我们可以看出来,在使用友元类的时候,多了几个步骤:

  1. 在代码一开始声明友元类B
    . 在这里插入图片描述

  2. 将内部函数在函数内部声明,然后放到类外面实现。这一步是必须做的
    在这里插入图片描述
    在这里插入图片描述

  3. 使用对象的方式不变
    在这里插入图片描述

  4. 在需要被设置成友元类的类中加一行:friend class A;A是主类B是友元类。
    在这里插入图片描述

七、 运算符重载

自定义类型,编译器不知道运算规则。

我们首先一个类,实现复数的一些运算。
直接看下面代码:


//运算符重载
//写一个复数类,

class Complex// 1+2i   拥有实部和虚部之分
{
public:
	//初始化实部
	Complex(int a, int b)//带参构造
	{
		this->_a = a;
		this->_b = b;
	}
	//打印这个复数
	void showComp()
	{
		cout << _a << "+" << _b << "i" << endl;
	}
private:
	int _a;            //实部
	int _b;            //虚部

};
int main()
{
	Complex c1(1, 2);
	Complex c2(3, 4);
	Complex c3;
	c1.showComp(); 
	c2.showComp();
	c3.showComp();
	return 0;
}

下面的代码将会以该代码为基础,进行添加修改。

从上面的代码我们可以看出,在这个代码中,我们可以实现复数的显示。但是,如果我们想进一步的实现c1和c2的相加将会报错。
如果大家把上述代码中的main换成如下形式:
现在我们想实现c3 = c1+c2;

int main()
{
	Complex c1(1, 2);
	Complex c2(3, 4);
	Complex c3;
	c1.showComp(); 
	c2.showComp();
	c3.showComp();
	c3 = c1 + c2;//我们多加了一个这个代码,为了实现复数的加法,我们知道该如何计算,但是这时候的编译器不知道。因为自定义类型,编译器不知道运算规则。
	return 0;
}

基础数据类型的运算规则,例如
Int main()
{
int a = 10;
int b = 20;
int c = a+b;
return c;
}
像这种基础数据类型,编译器是知道其运算规则的,随意无论我们怎么作,都会给出结果。
现在,如果我们想实现这个复数相加的功能,该如何办呢?
对的,我们可以使用友元函数,或者使用运算符重载。
先看看使用友元函数的方法:

//运算符重载
//写一个复数类,

class Complex// 1+2i   拥有实部和虚部之分
{
public:
	friend Complex add_C(Complex &c1, Complex &c2);
	//初始化实部
	Complex(int a = 0, int b = 0)//带参构造
	{
		this->_a = a;
		this->_b = b;
	}
	//打印这个复数
	void showComp()
	{
		cout << _a << "+" << _b << "i" << endl;
	}
private:
	int _a;            //实部
	int _b;            //虚部

};

Complex add_C(Complex &c1,Complex &c2)
{
	Complex temp(c1._a + c2._a, c1._b + c2._b);//但是,由于_a  _b是私有变量,因此,如果想使用这两个变量,就需要将该函数设置为友元函数,
	return temp;
}
int main()
{
	Complex c1(1, 2);
	Complex c2(3, 4);
	Complex c3;
	//c3 = c1 + c2;
	c3 = add_C(c1, c2);
	c3.showComp();
	return 0;
}

现在我们在尝试一下运算符重载的方法

在上一个代码的基础上,依然还是要使用友元函数的,但是我们加入了(operator 运算法)方法。

  • 直接看代码
#include <iostream>
using namespace std;


//运算符重载
//写一个复数类,

class Complex// 1+2i   拥有实部和虚部之分
{
public:
	friend Complex add_C(Complex &c1, Complex &c2);//由于那函数方法
	friend Complex operator+(Complex &c1, Complex &c2);//运算符重载方法
	//初始化实部
	Complex(int a = 0, int b = 0)//带参构造
	{
		this->_a = a;
		this->_b = b;
	}
	//打印这个复数
	void showComp()
	{
		cout << _a << "+" << _b << "i" << endl;
	}
private:
	int _a;            //实部
	int _b;            //虚部

};

//使用纯友元函数的方法
Complex add_C(Complex &c1, Complex &c2)
{
	Complex temp(c1._a + c2._a, c1._b + c2._b);//但是,由于_a  _b是私有变量,因此,如果想使用这两个变量,就需要将该函数设置为友元函数,
	return temp;
}
//使用运算符重载的方法
Complex operator +(Complex &c1,Complex &c2)
{
	Complex temp(c1._a + c2._a, c1._b + c2._b);//但是,由于_a  _b是私有变量,因此,如果想使用这两个变量,就需要将该函数设置为友元函数,
	return temp;
}
int main()
{
	Complex c1(1, 2);
	Complex c2(3, 4);
	Complex c3,c4;
	//c3 = c1 + c2;
	//使用友元函数方法
	//c3 = add_C(c1, c2);
	//使用运算符重载方法
	c3 = operator+(c1, c2);//其实,不限于该格式,只写写成+形式也可以,如下:
    c3 = c1+c2;//直接写成这样也是可以的
	c3.showComp();
c4. showComp();
	return 0;
}

  • 请大家仔细对比代码,可以发现不同的地方。
    在这里插入图片描述
    在这里插入图片描述
    运算符重载之后,只是添加了一个新的功能,以前新的功能仍然存在。大家可以在代码中自行举例子。

这时候买如果我们想计算
c5=c1 + 4;
该怎么办呢?大家不要思考了,直接写肯定会报错的。
这个时候我们显然还是需要重新再写运算符的。
大家运行一下下面的代码:


#include <iostream>
using namespace std;


//运算符重载
//写一个复数类,

class Complex// 1+2i   拥有实部和虚部之分
{
public:
	friend Complex add_C(Complex &c1, Complex &c2);//由于那函数方法
	friend Complex operator+(Complex &c1, Complex &c2);//运算符重载方法
	friend Complex operator+(Complex &c1, int num);//运算符重载方法
	//初始化实部
	Complex(int a = 0, int b = 0)//带参构造
	{
		this->_a = a;
		this->_b = b;
	}
	//打印这个复数
	void showComp()
	{
		cout << _a << "+" << _b << "i" << endl;
	}
private:
	int _a;            //实部
	int _b;            //虚部
};
//使用纯友元函数的方法
Complex add_C(Complex &c1, Complex &c2)
{
	Complex temp(c1._a + c2._a, c1._b + c2._b);//但是,由于_a  _b是私有变量,因此,如果想使用这两个变量,就需要将该函数设置为友元函数,
	return temp;
}
//使用运算符重载的方法
Complex operator +(Complex &c1,Complex &c2)
{
	Complex temp(c1._a + c2._a, c1._b + c2._b);//但是,由于_a  _b是私有变量,因此,如果想使用这两个变量,就需要将该函数设置为友元函数,
	return temp;
}
//重载
Complex operator+(Complex &c1, int num)
{
	Complex temp(c1._a + num, c1._b + 0);//但是,由于_a  _b是私有变量,因此,如果想使用这两个变量,就需要将该函数设置为友元函数,
	return temp;
}
int main()
{
	Complex c1(1, 2);
	Complex c2(3, 4);
	Complex c3,c5;
	//c3 = c1 + c2;
	//使用友元函数方法
	//c3 = add_C(c1, c2);
	//使用运算符重载方法
	c3 = operator+(c1, c2);
	c5 = c1 + 4;
	c3.showComp();
	c5.showComp();
	return 0;
}

在这里插入图片描述
在这里插入图片描述
我们上面讲的是运算符的外部重载方式,当然也有内部重载方式。
内部重载方式可以省掉一个左操作数;
但是这两种重载方式只能以一种方式存在,如果同时存在会报错,当然,我们也不会显得蛋疼去写两个。
内部重载方式就不介绍了,有兴趣的同学可以看看下面
https://www.bilibili.com/video/BV1JW411s72U?p=5
这个大牛的视频讲的很好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值