**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的。 - 现在的关键问题来了:我们是如何获取地址的?
- 就showinfo()函数而言,其实他不是一个没有形参的函数,而是内部含有一个指向对象t1和t2地址的指针。
- 然后,类的内部实现其实是一个结构体(struct)。但是结构体里面没有函数,只有变量。因此,结构体中是这个类里的所有数据。
- 对于普通成员函数,编译器会在其内部添加一个指向当前对象的指针。例如
上述例子类中的函数为:
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中的所有成员(包括私有成员)。
友元函数的定义方法:
- 在类中写函数的声明;
- 在函数声明前加 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);
}
这个时候,我们调用该函数就不会出错了。
注意点:
- 友元函数是类的外部函数,因此友元函数中没有this指针。
- 友元函数破坏类的封装性,尽量不要用。
- 友元函数的声明可以放到类的任何位置(必须放到类中,不能放在类外面),不受访问控制符的影响。解释一下:
我们在声明这个友元函数的时候,并没有将其写在,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);
}
仔细看一下上面这段代码,我们可以看出来,在使用友元类的时候,多了几个步骤:
-
在代码一开始声明友元类B
. -
将内部函数在函数内部声明,然后放到类外面实现。这一步是必须做的
-
使用对象的方式不变
-
在需要被设置成友元类的类中加一行: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
这个大牛的视频讲的很好。