C++ 面向对象 - 运算符重载与友元函数(头歌)

第1关:复数运算

任务描述
本关任务:设计一个复数类并实现复数的三种运算。

相关知识
所谓重载,就是赋予新的含义。函数重载可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载也是一个道理,同一个运算符可以有不同的功能。本关我们就一起来学习运算符重载的使用。

运算符重载
运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。

重载运算符声明方式如普通成员函数一样,只不过他的名字包含关键字 operator,以及紧跟其后的一个 C++ 预定义的操作符。格式如下:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第1张图片
例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第2张图片
并不是所有 C++ 中的运算符都可以支持重载,我们也不能创建一个新的运算符出来并且不能改变运算符操作对象的个数。有的运算符只能作为类成员函数被重载,而有的运算符则只能当作普通函数来使用:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第3张图片

运算符重载有两种方式:

1.使用外部函数进行运算符重载;

2.使用成员函数进行运算符重载。

运算符重载之外部函数
要调用运算符重载函数,有两种方法,一种是通过函数名调用,即operator+(t1,t2),另一种是在使用运算符的时候自动调用,这里介绍第二种。

就如同函数重载的最佳匹配规则,使用一个运算符时,会寻找名为operator<运算符>,且与当前操作数最佳匹配的那个重载版本进行调用。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第4张图片
至于运算符重载函数内部怎么实现(定义),那就可以根据需求来了,例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第5张图片
输出结果为:30

注意:在运算符重载函数中也是要考虑类成员访问性的问题的。

运算符重载之成员函数
运算符重载的函数也可以写到某个类中成为一个成员函数,格式与写在外面没有区别,但在参数列表上有一些差异。

成为成员函数的运算符重载函数的参数需要少写一个最左边的参数,而少的这个参数就由当前的对象代替。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第6张图片
输出结果为:30

注意:作为成员函数的运算符重载函数也会受访问性影响。

编程要求
在右侧编辑器中的Begin-End之间补充代码,设计一个复数类( Complex ),该类有两个成员变量和两个函数(成员变量访问性为私有,函数为公有),并重载+,-,*运算符,实现复数的加、减、乘运算,具体要求如下:

成员变量:float real,代表实部。

成员变量:float image,代表虚部。

构造函数:Complex(float r,float i),用两个参数设置 real 和 image 成员变量的值。

输出复数函数:void Print(),输出格式为:实部 +/- 虚部i,比如1+1i,0−5i。

测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:1 1 2 2(数据分为两组,前两个和后两个数分别表示一个复数 c1 和 c2,即c1=1+1i,c2=2+2i)
预期输出:

c1 = 1+1i
c2 = 2+2i
c1 + c2 = 3+3i
c1 - c2 = -1-1i
c1 * c2 = 0+4i
测试输入:0 0 -1 0.5
预期输出:

c1 = 0+0i
c2 = -1+0.5i
c1 + c2 = -1+0.5i
c1 - c2 = 1-0.5i
c1 * c2 = -0+0i
开始你的任务吧,祝你成功!

解析:本道题目是考察函数重载中的运算符重载。

这道题要求实现虚数的加法、减法以及乘法运算。所以我们要声明并定义3个重载函数。

这里有两种方法:

方法1:将重载函数写成类中的成员函数。

这里只展示重载函数的声明:

1. Complex operator+(Complex c1);

2. Complex operator-(Complex c1);

3. Complex operator*(Complex c1);

这里只传入一个参数是因为当前重载函数为类的成员函数,可以访问类中的变量,类中的成员变量也算作一个参数,所以只需要我们传入一个参数就行了

将以上三个函数写在Complex类里面作为成员函数。

代码如下:

#include 
using namespace std;

/********* Begin *********/
class Complex
{
	//复数类的声明
    public:
    	Complex();                                     // 默认构造函数
    	Complex(float r,float i);                      // 带参数构造函数
    	void Print();                                  // Print函数打印信息
        // 重载函数
    	Complex operator+(Complex &c1);                // 重载加法
    	Complex operator-(Complex &c1);                // 重载减法
    	Complex operator*(Complex &c1);                // 重载乘法
    
    private:
    	float real;                                    // 实部
    	float image;                                   // 虚部
};

//复数类的定义
Complex::Complex(){                                    // 默认构造函数定义,可以不写里面的内容(大括号里面的内容可以为空),但是一定要有定义
    real = 0;
    image = 0;
}

Complex::Complex(float r,float i){                     // 带参数构造函数
	real = r;
	image = i;
}

void Complex::Print(){                                 // 输出实部与虚部
	cout<=0){
  	cout<<"+"<> a >> b >> c >> d;
    Complex c1(a,b),c2(c,d);

	cout << "c1 = ";
	c1.Print();
	cout << "c2 = ";
	c2.Print();

	cout << "c1 + c2 = ";
    (c1 + c2).Print();
	cout << "c1 - c2 = ";
    (c1 - c2).Print();
	cout << "c1 * c2 = ";
    (c1 * c2).Print();
}

注意:方法1代码中的那个Complex();为默认构造函数,虽然这个函数在本题里没有太大作用,但是3个重载函数的第一句代码都是Complex c;这行代码会调用默认构造函数,如果不写默认构造函数就会报错。

方法2:将重载函数写成外部函数。

此时重载函数为外部函数,返回值类型为Complex,和上面一样。不同的是传入的参数需要多一个。因为当前重载函数是外部函数,不能直接访问类中的私有变量。

这里只展示重载函数的声明:

1. Complex operator+(Complex c1,Complex c2);

2. Complex operator-(Complex c1, Complex c2);

3. Complex operator*(Complex c1, Complex c2);

将以上三个函数写在类的外部作为外部函数。

代码如下:

#include
using namespace std;
/********* Begin *********/
class Complex{
	//复数类的声明
	public:
		Complex(float r,float i);               // 带参数的构造函数
		void Print();                           // 输出函数
        // 外部函数无法直接访问类中的私有变量,所以定义两个float类型的函数帮助外部函数进行私有变量的访问
		float getReal(){                        
			return real;
		}
		float getImage(){
			return image;
		}
		
	private:
		float real;
		float image;
};

//复数类的定义
Complex::Complex(float r,float i){                  // 带参数构造函数
	real = r;
	image = i;
}

void Complex::Print(){                              // 输出函数
	cout<=0){
  	cout<<"+"<> a >> b >> c >> d;
    Complex c1(a,b),c2(c,d);

	cout << "c1 = ";
	c1.Print();
	cout << "c2 = ";
	c2.Print();

	cout << "c1 + c2 = ";
    (c1 + c2).Print();
	cout << "c1 - c2 = ";
    (c1 - c2).Print();
	cout << "c1 * c2 = ";
    (c1 * c2).Print();
}

注意:方法2相比于方法1,默认构造函数可以不写,因为在重载函数中我直接调用了带参数的构造函数。但是需要添加两个float类型函数帮助外部函数访问类中的私有变量。


第2关:病毒复制

任务描述
本关任务:设计一个病毒类,实现病毒检测功能。

相关知识
为了完成本关任务你需要掌握如何重载!=和==运算符以及拷贝构造函数的使用。

重载 != 和 == 运算符
!=和==都属于关系运算符,因此返回值都是布尔类型( bool ),并且它们都是双操作数运算符,因此重载它们的方式如下:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第7张图片
输出结果为:


重载==运算符时,t1 中成员 A 的值与 t2 中成员 A 的值并不相等,因此返回结果0,因此重载!=运算符时,返回结果为1。

拷贝构造函数
复制构造函数是构造函数的一种,也称拷贝构造函数,它只有一个参数,参数类型是本类的引用。它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。

拷贝构造函数一般用于实现对象语义上的拷贝。当一个对象的内部有动态分配的资源时,就要考虑是不是要提供一个拷贝构造函数以实现对象的深度拷贝了。

如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第8张图片
例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第9张图片
拷贝构造函数通常用于下列三种情况:

1.用一个对象去初始化一个同类对象时,可以是通过构造函数传入给一个同类对象,还可以是使用赋值运算符=。

2.将一个对象作为参数传递给一个函数,而且形式参数不是这个类型的指针或引用。

3.将一个对象作为返回值返回,而且返回值类型不是这个类型的指针或引用。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第10张图片
输出结果为:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第11张图片
注意:赋值之后没有 Copy 的输出,因为 t4 不是在初始化的时候使用赋值运算符的。

编程要求
在右侧编辑器中的Begin-End之间补充代码,设计病毒类( Virus ),实现病毒检测功能,具体要求如下:

1.成员变量:int Gen,代表当前病毒对象的年龄,默认值为 0。

2.拷贝构造函数:Virus(const Virus &v),拷贝到一个新的病毒对象时,并将新的病毒对象的成员变量年龄在原来病毒对象的年龄上加 1。

3.重载==运算符:bool operator==(const int& g,const Virus &v),用来比较g==virus[i],以找出年龄为参数 g 的病毒,并统计计数。

测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:0 0

预期输出:

年龄:0 数量:1
年龄:1 数量:2
年龄:2 数量:0


输出结果说明:平台会先创建一个病毒1对象,年龄为0,然后拷贝两个病毒1对象,因此这两个新的病毒的年龄就为1。

开始你的任务吧,祝你成功!

解析:本题考查的是重载函数与拷贝构造函数的应用。

1. 这道题的重载函数声明为: bool operator==(const int &g, const Virus &v);

这是一个返回值类型为bool类型的函数,返回值要么是真(True),要么是假(False)。

2. 使用拷贝构造函数时,传入的参数为当前类的一个对象。

(1)当我们在主函数中定义一个类的对象的同时传入类的变量对该对象进行初始化时会直接调用拷贝构造函数。

(2)当我们在主函数中定义一个类的对象的同时使用‘=’对该对象进行初始化,会调用拷贝构造函数。

(3)当某个函数的返回值为当前类的一个对象,会调用拷贝构造函数。

注意:如果主函数定义了一个当前类的对象,但是又并没有同时进行初始化,这个时候系统调用的应该是默认构造函数而不是拷贝构造函数。

代码如下:

#include 
using namespace std;

/********* Begin *********/
class Virus
{
	//病毒类的声明
    public:
        Virus();
        Virus(Virus &v);
        int getGen(){
        	return Gen;
		}
	private:
		int Gen;
};
//病毒类的定义以及其他内容
Virus::Virus(){
    Gen = 0;
}

Virus::Virus(Virus &v){
    Gen = v.getGen() + 1;
}

bool operator==(int g, Virus &v){
    return g==v.getGen();
}
/********* End *********/

int main()
{
	int i,j;
	cin >> i >> j;
	Virus vs[3];
	vs[0] = Virus();
	vs[1] = Virus(vs[i]);
	vs[2] = Virus(vs[j]);
	
	for(int g = 0;g < 3;g++)
	{
		int count = 0;
		for(int i = 0;i < 3;i++)
		{
			if(g == vs[i])
				count++;
		}
		cout << "年龄:" << g << " 数量:" << count << endl;
	}
}

第3关:学生信息转换

任务描述
本关任务:设计学生和教师两个类,并实现它们之间的转换。

相关知识
为了完成本关任务,你需要掌握友元函数、友元类以及转换构造函数。

友元函数
有时候我们希望某个函数能访问一个类的非公有成员,但又不想把它做成这个类的成员函数,这个时候就可以使用友元。

如果要将一个函数变成一个类的友元,只需要在类中函数前加一个 friend 关键字来声明函数即可,并且访问性不受限制。即表现形式为:


但这个友元函数他不属于该类的成员函数,他是定义在类外的普通函数,只是在类中声明该函数可以直接访问类中的 private 或者 protected 成员。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第12张图片
输出结果为:a = 100, b = 2000

友元类
C++ 中也允许声明一个类为某个类的友元类,方法和声明友元函数旗鼓相当。但是需要注意的是,作为友元的类的声明必须在友元类的声明之前。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第13张图片
有时候作为友元的类的声明不便放在友元声明之前,这个时候就可以使用前置声明。不过前置声明的类只是一个名字(或者说不完全类型),不能访问它内部的内容。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第14张图片
最后,友元声明还有如下限制:

1. 友元关系不能被继承。

2. 友元关系是单向的,不具有交换性。若类 B 是类 A 的友元,类 A 不一定是类 B 的友元。

3. 友元关系不具有传递性。若类 B 是类 A 的友元,类 C 是 B 的友元,类 C 不一定是类 A 的友元。

转换构造函数
一个构造函数接收一个不同于其类类型的形参,可以视为将其形参转换成类的一个对象,像这样的构造函数称为转换构造函数。因此转换构造函数的作用就是将一个其他类型的数据转换成一个类的对象。

除了创建类对象之外,转换构造函数还为编译器提供了执行隐式类型转换的方法。只要在需要类的类型值的地方,给定构造函数的形参类型的值,就将由编译器执行这种类型的转换。

转换构造函数是构造函数的一个特例,当一个构造函数的参数只有一个,而且是一个其他类型的 const 引用时,它就是一个转换构造函数。

例如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第15张图片
有了转换构造函数,就可以实现不同类型之间的类型转换了,比如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第16张图片
注意:转换构造函数只能有一个参数。如果有多个参数,就不是转换构造函数。

编程要求
在右侧编辑器中的Begin-End之间补充代码,设计学生和教师两个类,类中成员变量都为私有,成员函数都为公有,并实现它们之间的转换,具体要求如下:

1. 学生类( Student )

        编号:int number

        姓名:string name

        性别:string sex

        带参构造函数:Student(int num,string nam,string se),用三个参数对应初始化内部的三个成员变量。

        输出函数:void Print()函数,输出学生信息,格式为:学生:name,编号:number,性别:sex。

2. 教师类( Teacher )

        与 Student 类有三个同样的成员变量

        输出函数:void Print(),输出教师信息,格式为:教师:name,编号:number,性别:sex的格式打印三个成员变量。

        转换构造函数,用于从 Student 类转换到 Teacher 类,它将 Student 对象的成员变量对应复制 Teacher 对象的成员变量中。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:1 王大明 男
预期输出:

学生:王大明,编号:1,性别:男
教师:王大明,编号:1,性别:男
测试输入:2 雷珺 女
预期输出:

学生:雷珺,编号:2,性别:女
教师:雷珺,编号:2,性别:女
开始你的任务吧,祝你成功!

解析3:本道题需要我们学会对友元函数以及转换构造函数的声明与定义。

那什么是友元函数呢?我们在主函数中定义了一个类的对象,但是此对象无法访问类中的私有变量,我们可以将变量改成公有变量,但是这样数据就不安全。

访问私有变量有两种简单方法:

1. 通过成员函数访问。

这个方法就是在类中定义一个与私有变量相同类型返回值函数。

如:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第17张图片

2. 通过友元函数访问。

在类中写一个友元函数,这个函数能够访问私有变量。

C++ 面向对象 - 运算符重载与友元函数(头歌)_第18张图片

代码如下:

#include 
#include 
using namespace std;

/********* Begin *********/
// 前置声明 Teacher 类
class Teacher;                            // 友元类前置声明 

class Student
{	
	friend class Teacher;            // 友元类声明 
	//学生类的声明
    public:
        Student(int num,string nam,string se);
        void Print();
        
    private:
        int number;
        string name;
        string sex;
};
//学生类的定义
Student::Student(int num,string nam,string se){
    number = num;
    name = nam;
    sex = se;
}

void Student::Print(){
    cout<<"学生:"<> number >> name >> sex;
    Student st(number,name,sex);
    st.Print();
    Teacher t = (Teacher)st;
    t.Print();
}

第4关:矩阵运算

任务描述
本关任务:设计一个矩阵类,并实现简单的矩阵运算。

相关知识
完成本关需要具备的知识介绍请参见第一、二关卡。

编程要求
在右侧编辑器中的Begin-End之间补充代码,设计一个矩阵类( Matrix ),并实现矩阵的简单运算,具体要求如下:

1. 成员变量:这一部分学员可以自由发挥,但要求都是私有成员。

2. 成员函数:

        构造函数:Matrix(int r,int c),参数 r 和 c 分别代表矩阵的行和列。

        全部设值函数:void Fill(int value),函数将矩阵内所有的元素都设置为参数 value 的值。

        指定位置设值函数:void Set(int r,int c,int value),函数将矩阵第 r 行 c 列的元素设置为 value 的值。

        获取元素函数:int Get(int r,int c)函数,函数返回矩阵第 r 行 c 列的元素。

        打印函数:void Print(),函数按照矩阵的形状打印出矩阵内容,每一个值后跟着一个空格。比如一个2x4元素全为1的矩阵,打印结果为(更明显表示格式,空格均用下划线_代替):


3. 普通函数:

        Matrix operator+(Matrix &m1,Matrix &m2)函数,重载Matrix类的加法运算符,实现矩阵的加法运算。

        Matrix operator-(Matrix &m1,Matrix &m2)函数,重载Matrix类的减法运算符,实现矩阵的减法运算。

        Matrix operator*(Matrix &m1,Matrix &m2)函数,重载Matrix类的乘法运算符,实现矩阵的乘法运算。

测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:

测试输入:1 1
预期输出:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第19张图片
测试输入:2 2
预期输出:

C++ 面向对象 - 运算符重载与友元函数(头歌)_第20张图片
开始你的任务吧,祝你成功!

解析:本道题综合了前三道题的知识,但是并没有都用上。

代码如下:

#include 
#include 
using namespace std;

/********* Begin *********/
class Matrix
{
	//矩阵类的声明
    public:
        Matrix(int r,int c);
        void Fill(int value);
        void Set(int r,int c,int value);
        int Get(int r,int c);
        void Print();
        int getRow(){
        	return row;
		}
		int getColumn(){
			return column;
		}

    private:
    	int row;
    	int column;
    	int jz[47][47];  
};
//矩阵类的定义
Matrix::Matrix(int r,int c){
	row = r;
	column = c;
}

void Matrix::Fill(int value){
	int i,j;
	for(i=0;i<47;i++){
		for(j=0;j<47;j++){
			jz[i][j] = 0;
		}
	}
	
	for(i=0;i> i >> j;									
    Matrix m1(i,j),m2(i,j),m3(j,i);		
    m1.Fill(1);                            
    m2.Fill(2);							
	m3.Fill(0);								
	
	// 将矩阵m3行和列相等的元素设置为行号+1 
    for(int s = 0 ; s < i ; s++){
		for(int c = 0 ; c < j ; c++){
			if(s==c)
				m3.Set(s,c,s+1);
		}
    }
	//m1.Print();
    //m2.Print();
    //m3.Print();
	cout << "m1 + m2 :" << endl ;
    (m1 + m2).Print();
	cout << "m1 - m2 :" << endl ;
    (m1 - m2).Print();
	cout << "m1 * m3 :" << endl ;
    (m1 * m3).Print();
    
}

备注:以上代码是我个人所写,不是标准答案!

你可能感兴趣的:(c++,java,算法)