C++面向对象 - 运算符重载、友元函数和STL

复数运算

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

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

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

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

函数类型 operator 运算符名称 (形参表列)
{
// 对运算符的重载处理
}
例如:

class Test{ /* 类声明 */ };
Test operator+(Test t1,Test t2); // 重载 + 运算符,这个运算符要求两个操作数
并不是所有 C++ 中的运算符都可以支持重载,我们也不能创建一个新的运算符出来并且不能改变运算符操作对象的个数。有的运算符只能作为类成员函数被重载,而有的运算符则只能当作普通函数来使用:

不能被重载的运算符有:. 、.* 、:: 、?: 、sizeof

只能作为类成员函数重载的运算符有:() 、[] 、-> 、=

运算符重载有两种方式:

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

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

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

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

例如:

class Test{ /* 类声明 / };
class D : public Test {}; //创建一个 Test 类的子类
//外部函数
Test operator+(Test t1,Test t2){ /
一些操作 */ }
int main()
{
Test t1,t2;
Test t3 = t1 + t2; // 最佳匹配是 operator+(Test,Test)
D d1,d2;
D d3 = d1 + d2; // 最佳匹配也是 operator+(Test,Test)
}
至于运算符重载函数内部怎么实现(定义),那就可以根据需求来了,例如:

class Test
{
public:
int a;
};
Test operator+(Test &t1,Test &t2)
{
// 重载加法运算符,实际对 Test 类中的 a 成员变量进行加法运算
Test t;
t.a = t1.a + t2.a;
return t;
}
int main()
{
Test t1,t2;
t1.a = 10; t2.a = 20;
cout << (t1 + t2).a <<endl; // 调用 operator(Test&,Test&)的重载版本
}
输出结果为:30

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

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

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

例如:

class Test
{
public:
int a;
Test operator+(Test& t2); // 少了左边的一个参数
};
Test Test::operator+(Test &t2)
{
Test t;
t.a = a + t2.a; // 当前对象代替了原本的第一个参数
return t;
}
int main()
{
Test t1,t2;
t1.a = 10; t2.a = 20;
cout << (t1 + t2).a << endl; // 调用的是 Test 类成员函数的那个重载版本
}
输出结果为:30

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

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

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

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

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

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

.h

#include <iostream>
using namespace std;

/********* Begin *********/
class Complex
{
	//复数类的声明
public:
    Complex(float r,float i)
		:real(r),image(i)
	{ }
	void Print();
	Complex();

	Complex& operator += (const Complex&);
	// float real () const { return re; }
	// float imag () const { return im; }

	Complex operator + (Complex &obj);
	Complex operator - (Complex &obj);
	Complex operator * (Complex &obj);
	Complex operator / (Complex &obj);
	
private:
	float real;
	float image;
    
};

Complex::Complex()
{ }

Complex Complex::operator + (Complex &obj)
{
return Complex ( real+ obj.real,
image+ obj.image);
}

Complex Complex::operator - (Complex &obj)
{
return Complex ( real- obj.real,
image- obj.image);
}

Complex Complex::operator * (Complex &obj)
{
Complex c;
c.real=real*obj.real-image*obj.image;
c.image=image*obj.real+obj.image*real;
return c;
}


Complex Complex::operator / (Complex &obj)
{
Complex c;
c.real=real*obj.real+image*obj.image;
c.image=image*obj.real-obj.image*real;
float A=obj.real*obj.real+obj.image*obj.image;
c.real=c.real/A;
c.image=c.image/A;
return c;
}

void Complex::Print()
{
	if(Complex::image<0)
	{
		cout <<Complex::real<<Complex::image<<'i'<<endl;
	}
	else
		cout <<Complex::real<<'+'<<Complex::image<<'i'<<endl;
}






//复数类的定义



/********* End *********/

.cpp

#include "usr.h"
int main()
{
	float a,b,c,d;
	cin >> 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();
}

病毒复制

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

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

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

class T
{
public:
int A;
};
bool operator==(const T &t1,const T &t2) // 重载 == 运算符
{
return t1.A == t2.A;
}
bool operator!=(const T &t1,const T &t2) // 重载 != 运算符
{
return t1.A != t2.A;
}
int main()
{
T t1,t2;
t1.A = 10; t2.A = 20;
cout << "t1 == t2? " << (t1 == t2) << endl;
cout << "t1 != t2? " << (t1 != t2) << endl;
}
输出结果为:

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

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

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

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

类名::类名(const 类名& 对象名)
{
函数体;
}
例如:

class Test
{
public:
Test();
Test(const Test& t); // 拷贝构造函数,参数不带 const 也行
};
拷贝构造函数通常用于下列三种情况:

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

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

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

例如:

/* 类声明同上 */
Test::Test(){}
Test::Test(const Test& t)
{
cout << “Copy” << endl;
}
Test Fun(Test t)
{
cout << “返回值” <<endl;
return t;
}
int main()
{
Test t;
cout << “直接调用” << endl;
Test t2(t);
cout << “初始化” << endl;
Test t3 = t;
cout << “函数调用” <<endl;
Fun(t);
cout << “赋值” << endl;
Test t4;
t4 = t;
}
输出结果为:

直接调用
Copy
初始化
Copy
函数调用
Copy
返回值
Copy
赋值
注意:赋值之后没有 Copy 的输出,因为 t4 不是在初始化的时候使用赋值运算符的。

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

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

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

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

.h


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

bool operator==(const int &g,const Virus &v1)     // 重载 == 运算符
{
    if(v1.Gen == g)
		return 1;
	else
		return 0;
}


Virus::Virus(const Virus &v)
{
	this->Gen = v.Gen+1;
}
/********* End *********/

.cpp

#include <iostream>
#include "usr.h"
using namespace std;

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;
	}
}

学生信息转换

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

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

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

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

friend <返回类型> <函数名> (<参数列表>);
但这个友元函数他不属于该类的成员函数,他是定义在类外的普通函数,只是在类中声明该函数可以直接访问类中的 private 或者 protected 成员。

例如:

class Test{
private:
int a;
protected:
int b;
public:
friend void fun(Test t); // 友元函数
};
void fun(Test t)
{
t.a = 100;
t.b = 2000;
cout << "a = " << t.a << ", b = " << t.b << endl;
}
int main()
{
Test test;
fun(test);
return 0;
}
输出结果为:a = 100, b = 2000

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

例如:

class TestFriend
{
private:
int a;
}; // TestFriend 类的声明在 Test 类之前
class Test
{
private:
int a;
friend class TestFriend; // 声明友元类
};
有时候作为友元的类的声明不便放在友元声明之前,这个时候就可以使用前置声明。不过前置声明的类只是一个名字(或者说不完全类型),不能访问它内部的内容。

例如:

class TestFriend; // 前置声明 TestFriend 类,只是一个名字
class Test
{
private:
int a;
friend class TestFriend; // 声明友元类
friend void TestFriend::Func(); // 尝试将 TestFriend 类的成员函数 Func 作为友元函数,但由于 TestFriend 类目前只有前置声明,所以会出错。
};
class TestFriend // TestFriend 类的声明在 Test 类之后
{
private:
int a;
public:
void Func();
};
最后,友元声明还有如下限制:

友元关系不能被继承。

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

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

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

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

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

例如:

class T1{};
class T2
{
public:
T2(const T1 &t); // 从 T1 转换到 T2 的转换构造函数
};
有了转换构造函数,就可以实现不同类型之间的类型转换了,比如:

/* 类定义同上 */
int main()
{
T1 t1;
T2 t2= (T2)t1; // 用类型转换语法,从 T1 转换到 T2
T2 t3(t1); // 或者直接调用转换构造函数
}
注意:转换构造函数只能有一个参数。如果有多个参数,就不是转换构造函数。

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

学生类( Student )

编号:int number

姓名:string name

性别:string sex

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

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

教师类( Teacher )

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

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

转换构造函数,用于从 Student 类转换到 Teacher 类,它将 Student 对象的成员变量对应复制 Teacher 对象的成员变量中。

.h

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

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

class Student
{
	//学生类的声明
    public:
    Student(int num,string nam,string se);
	void Print();
    int get_num()
    {
        return number;
    }
    string get_name()
    {
        return name;
    }
    string get_sex()
    {
        return sex;
    }
private:
    int number;
    string name;
    string sex;
   
    
};
//学生类的定义




class Teacher
{
	//教师类的声明
    
public:
    Teacher() {};
    Teacher(Student &);
    Teacher(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<<"学生:"<<name<<",编号:"<<number<<",性别:"<<sex<<endl;
}
Teacher::Teacher(int num,string nam,string se)
{
    number=num;
    name=nam;
    sex=se;
}
void Teacher::Print()
{
    cout<<"教师:"<<name<<",编号:"<<number<<",性别:"<<sex<<endl;
}
Teacher::Teacher(Student &stu)
{
    number=stu.get_num();
    name=stu.get_name();
    sex=stu.get_sex();
}
/********* End *********/

.cpp

#include "usr.h"
int main()
{
	int number;
	string name,sex;
	cin >> number >> name >> sex;
    Student st(number,name,sex);
    st.Print();
    Teacher t = (Teacher)st;
    t.Print();
}

矩阵运算

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

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

矩阵运算参考资料

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

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

成员函数:

构造函数: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的矩阵,打印结果为(更明显表示格式,空格均用下划线_代替):

1_1_1_1_
1_1_1_1_
普通函数:

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

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

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

.h

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

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

	friend Matrix operator + (Matrix& m1,Matrix &m2);    
    friend Matrix operator - (Matrix& m1,Matrix &m2);   
    friend Matrix operator * (Matrix& m1,Matrix &m2); 

private:
    int row, column;        //成员变量行和列
	int pt[10][10]; 
    
};
//矩阵类的定义
Matrix::Matrix()
{

}
Matrix::Matrix(int r,int c)
{
    row = r;
    column = c;
}

void Matrix::Print(){     //输出矩阵
	for(int i = 0; i < row; i++)
	{
		for (int j = 0; j < column; j++)
			cout << this->pt[i][j] << " ";
		cout << endl;
	}
}
void Matrix::Fill(int value)
{
    for (int i = 0; i < row; i++)
    {
		for (int j = 0; j < column; j++)
			this->pt[i][j] =value;
    }
}
void Matrix::Set(int r,int c,int value)
{
    this->pt[r][c] =value;
}
int Matrix::Get(int r,int c)
{
    return this->pt[r-1][c-1];
}


Matrix operator+(Matrix& m1,Matrix &m2)
{
    //实现矩阵加法
    Matrix temp(m1.row, m1.column);
	for (int i = 0; i < m1.row; i++){
		for (int j = 0; j < m1.column; j++)
			temp.pt[i][j]  = m1.pt[i][j] + m2.pt[i][j];
	}
	return temp;
   
    
}

Matrix operator-(Matrix& m1,Matrix &m2)
{
    //实现矩阵减法
    Matrix temp(m1.row, m1.column);
	for (int i = 0; i < m1.row; i++){
		for (int j = 0; j < m1.column; j++)
			temp.pt[i][j] = m1.pt[i][j] - m2.pt[i][j];
	}
	return temp;
    
    
}

Matrix operator*(Matrix& m1,Matrix &m2)
{
    //实现矩阵乘法
    Matrix temp(m1.row, m2.column);
	for (int i = 0; i < m1.row; i++) {
			for (int j = 0; j < m2.column; j++) {
				long sum=0;
				for (int k = 0; k < m1.column; k++) {
					sum += m1.pt[i][k] * m2.pt[k][j];    //累加第i行与第j列的元素乘积
				}
				temp.pt[i][j] = sum;   //赋给i,j元素
			}
		}
		return temp;
    
}
/********* End *********/

.cpp

#include "usr.h"
int main()
{
	int i,j;
	cin >> i >> j;
    Matrix m1(i,j),m2(i,j),m3(j,i);
    m1.Fill(1);
    m2.Fill(2);
	m3.Fill(0);
    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();
    
}

动态学生信息管理

任务描述
本关任务:编写一个能动态管理学生信息的程序。

相关知识
STL 的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。

本实训我们就一起来学习 vector 容器、sort 和 find 算法以及迭代器的使用。

vector 容器
vector 是顺序容器的一种。vector 是可变长的动态数组,支持随机访问迭代器,所有 STL 算法都能对 vector 进行操作。要使用 vector,需要包含头文件 vector。

vector 容器在实现时,动态分配的存储空间一般都大于存放元素所需的空间。例如,哪怕容器中只有一个元素,也会分配 32 个元素的存储空间。这样做的好处是,在尾部添加一个新元素时不必重新分配空间,直接将新元素写入适当位置即可。

它的常用构造函数如下:

int main()
{
vector v; // 无参数版
vector v2(10); // 初始容量为 10 的 vector,每个元素为都为 string 的默认值
vector v3(10,“abc”); // 初始容量为 10,且每一个元素都为 abc
}
如果要访问或者修改 vector 的内容,可以用如下的函数:

vector v;
v.push_back(10); // 将一个元素添加到容器的末尾
v.push_back(20);
cout << v[0] << endl; // 访问索引为 0 的元素,即 10
v.erase(v.begin() + 1); // 删除索引为 1 的元素,begin() 函数的含义见下文
v.insert(v.begin() + 1,30); // 在索引为 1 的元素之前插入一个元素
v.clear(); // 删除所有元素
同时,vector 还有如下的成员函数用于查询元素数量:

int size = v.size(); // 返回元素数量
bool isem = v.empty(); // 容器为空则返回 true
vector 的成员函数不仅仅只有这些,感兴趣的同学可以查阅其他资料进行学习。

迭代器
STL 中的容器都提供了迭代器( iterator )成员类型,它就像一个指针,使用它能按照一种特定的顺序遍历容器中的所有元素。

一般来说,当你对一个迭代器 it 进行it++自增操作时,就能进入到下一个元素,进行it–自减操作时,就能回到上一个元素。有些迭代器还支持it+=2,it-=2这样跨多个元素的操作。并对它使用*it取值操作,就能获得当前的元素值。

上文提到的begin()函数,返回的其实就是一个指向第一个元素的迭代器,与之对应的还有一个end()函数,它返回一个指向最后一个元素之后不存在的元素的迭代器,有了这两个函数,我们就能访问 vector 中的所有元素了,代码如下:

vector v(5,10); // 容量为 5,元素值都是 10
for(vector::iterator it = v.begin();it != v.end();it++)
cout << *it << " ";
输出结果为:10 10 10 10 10

此时我们也可以知道,vector 的 erase ,insert 函数的参数实际上都是迭代器类型。

注意:在使用迭代器迭代一个容器的过程中,使用修改容器内容的函数可能会使得迭代器无效,因此尽量不要在这个过程中修改容器内容。

sort 算法
在很多应用中,排序都是至关重要的,而且很多 STL 算法也只适用于有序对象序列。

sort 算法位于头文件,它接收两个迭代器参数 first 和 last,代表要排序的范围,为左闭右开,即[first,last),然后将这个范围里的元素按照升序排列,并要求迭代器的元素类型重载了此类型对象间用于比较的<运算符,即表现形式为:

sort(first,last);
例如:

vector v;
v.push_back(2);
v.push_back(1);
int vs[]={2,1};
sort(v.begin(),v.end()); // 对 vector 排序
sort(vs,vs + 2); // 对数组排序
排序后两个容器内的元素都是:1 2

注意:指针也可以看做是一种迭代器,所以对于数组也是可以使用这个函数的。

find 算法
find 算法位于头文件,它接收三个参数,前两个迭代器类型的 first ,last 参数代表范围,左闭右开,第三个 value 参数代表要查找的内容,因此元素类型必须重载用于元素间比较 value 值的==运算符,即表现形式为:

find(first,last,value);
它返回的也是一个迭代器,如果找到了这个元素,则返回指向这个元素的迭代器,如果没有,则返回 last 参数值。

例如:

vector v;
for (size_t i = 0; i < 10; ++i)
{
v.push_back(i+1);
}
v.push_back(10);
vector::iterator iLocation = find(v.begin(), v.end(), 10); // find 操作查找等于 10 的元素,并返回指向该元素的迭代器,如果没有找到,返回指向集合最后一个元素的迭代器。
if (iLocation != v.end())
{
cout << “找到元素 10” << endl;
}
cout << “前一个元素为:” << *(–iLocation) << endl;
输出结果为:

找到元素 10
前一个元素为:9
注意:find 函数在比对元素的时候,==运算符的第二个参数是要找的内容,即参数 value。

编程要求
在右侧编辑器中的Begin-End之间补充代码,设计一个学员信息表,并在主函数中读取输入,根据输入的内容作出相应的动作,输入内容共有4种格式,具体如下:

A 姓名 分数:向信息表的末尾添加一条记录,内容为姓名 分数,分数均为整数。如果表中已有同一个人的记录,那就只更新分数。

R 姓名:删除表中指定姓名的条目,不存在则不做处理。

P,按照姓名 分数 的格式打印整个信息表,每条记录占1行。如果表为空,也要输出空行。

S,将整个信息表的数据按照分数的降序排序。

注意:为了保证排序结果稳定,输入的数据保证不会有两条记录有相同的分数。

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

/********* Begin *********/
//自定义的类和者其他内容
struct Student
{
    string name_;
    int score_;
};

bool cmp(Student st1, Student st2)
{
    return st1.score_ > st2.score_;
}

/********* End *********/

int main()
{
    /********* Begin *********/
    char ch;
    string name;
    int score;
    vector<Student> st;
    //读取输入数据,执行动作
    while (cin >> ch)
    {
        switch (ch)
        {
            case 'A':
            {
                cin >> name >> score;  
                bool flag = true;
                for (int i = 0; i < st.size(); i++)
                {
                    if (st[i].name_ == name)
                    {
                        st[i].score_ = score;
                        flag = false;
                    }
                }
                
                Student t;
                t.name_ = name;
                t.score_ = score;
                if (flag) st.push_back(t);
                break;
            }
            case 'R':
            {
                cin >> name;  
                for (int i = 0; i < st.size(); i++)
                    if (st[i].name_ == name)
                    {
                        st.erase(st.begin() + i);
                        break;
                    }
                break;
            }
            case 'P':
            {
                for (int i = 0; i < st.size(); i++)
                {
                    cout << st[i].name_ << " " << st[i].score_ << endl;  
                }
                if (st.size() == 0) cout << "[空]" <<endl;
                break;
            }
            case 'S':
            {
                sort(st.begin(), st.end(), cmp);
                break;
            }
            default: break;
        }
    }
    return 0;
    /********* End *********/
}

还原键盘输入

任务描述
本关任务:编写一个能根据键盘操作记录还原实际输入的小程序。

相关知识
为了完成本关任务,你需要掌握 list 容器的使用。

list 容器
list 位于头文件,是 STL 提供的一种顺序存储的容器,它不支持通过位置索引访问,但能高效的进行插入、删除元素操作,通常由双向链表实现。

在这里插入图片描述

图1 - list 采用的双向循环链表结构
list对象的创建,初始化赋值方法与vector相同,它常用的构造函数如下:

list l(); // 无参构造函数
list l2(10); // 包含 10 个默认值 0 元素
list l3(10,123); // 包含 10 个 123 元素
由于链表中的数据需要一个个元素进行遍历,因此,list 元素的遍历使用迭代器的方式进行,通过它的 begin ,end 函数用于获取指向第一个元素的迭代器和指向最后一个元素之后的迭代器。例如:

int main()
{
//将数据插入链表
list li(5,123);
list::iterator i = li.begin(),j=li.end();
//遍历打印链表元素
for(i ; i != j ; i++ )
{
cout << (*i) << endl;
}
return 0;
}
输出结果为:

123
123
123
123
123
如果要修改容器内容,可以使用以下函数:

list l;
l.push_back(1); // 添加到链尾
l.push_front(2); // 添加到链头
l.pop_back(); // 删除链尾元素
l.pop_front(); // 删除链头元素
l.insert(l.begin(),3); // 在指定迭代器之前添加一个元素
l.erase(l.begin()); // 删除指定迭代器所指的元素
list 的迭代器有一点特别,当对容器进行修改时,只要不是删除这个迭代器元对应的元素,这个迭代器都不会失效。

例如:

list l;
list::iterator it = l.begin();
l.insert(it,10); // 执行之后 it 依然有效,并且现在 it 不再与l.begin() 相等
l.erase(it-1); // 执行之后 it 依然有效
l.erase(it); // 现在 it 无效了
如果要查询 list 的元素数量,可以使用以下函数:

int size = l.size(); // 返回元素个数
bool isem = l.empty(); // 容器为空时返回 true
list 的成员函数不仅仅只有这些,感兴趣的同学可以查阅其他资料进行学习。

编程要求
在右侧编辑器中的Begin-End之间补充代码,读取输入的键盘操作记录,并根据操作记录还原最后的输入结果。每一次的操作记录为一行字符串,字符串的内容分为以下几类:

大小写字母,数字:代表用户在输入光标的左侧输入了一个具体字符,光标保持在新输入字符的右侧。

<,>字符:代表用户按下了左键或者右键,作用是输入光标向左或者向右移动一个位置。

[,]字符:代表用户按下了 Home 键或者 End 键,作用是移动输入光标到最开始或者最末尾的位置。

注意:每一次操作记录开始的时候都是没有输入的且输入光标只能在已输入内容的两端和内部移动。

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

int main()
{
    /********* Begin *********/
    //读取输入,解析并输出复原后的输出
    char ch;
    list<char> li;
    list<char>::iterator it = li.begin();

    while((ch = getchar()) != EOF)
    {
        switch(ch)
        {
            case '\n':
            {
                for(list<char>::iterator i = li.begin();i != li.end();i++)
                {
                    cout << *i;
                }
                cout << endl;
                li.clear();
                it = li.begin();
                break;
            }
            case '<':
            {
                it--;
                break;
            }
            case '>':
            {
                it++;
                break;
            }
            case '[':
            {
                it = li.begin();
                break;
            }
            case ']':
            {
                it = li.end();
                break;
            }
            default: 
            {
                li.insert(it, ch);
                break;
            }
        }
    }
    for(list<char>::iterator i = li.begin(); i != li.end(); i++) 
        cout << *i; 
    cout<<endl; 

    /********* End *********/
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值