结构体能够将一些不同类型的信息“捆绑”聚合成一个整体,例如可以用一个结构体来记录学生的姓名(字符串类型)、性别(char类型)、年龄(int类型)等信息。结构体就像int、double一样,是一种(自定义的)数据类型。
一、结构体的基本使用方法
先来看一个问题:P5740 【深基7.例9】最厉害的学生
分析:使用“打擂台”方式求极值,只不过这里要记录下最高分的学生的姓名和三科成绩:
#include<iostream> #include<string> using namespace std; int main() { string name,ans_name; int n,chinese,math,english,total; int ans_chinese,ans_math,ans_english,ans_total; ans_total = 0; cin>>n; for(int i=1;i<=n;i++){ cin>>name>>chinese>>math>>english; total = chinese+math+english; if(total>ans_total){ ans_total = total; ans_name= name; ans_chinese = chinese; ans_math = math; ans_english = english; } } cout<<ans_name<<" "<<ans_chinese<<" "<<ans_math<<" "<<ans_english<<endl; return 0; }
上面的程序使用了5个变量来存储每次输入的学生姓名、语文、数学、英语和总分5项信息,同样用5个变量记录了最高分学生的5项信息,“打擂”过程中,遇到新的最高分,记录最高分学生的5项信息都要更新,这样操作有点繁琐。
可以使用结构体将学生的5项信息“捆绑打包”聚合成一个自定义数据类型:
#include<iostream> #include<string> using namespace std; //定义一个结构体Student来记录学生信息,Student是一个自定义数据类型 //结构体中name、chinese等是Student的成员变量(书写就像定义普通变量一样) struct Student{ string name; int chinese,math,english,total; }; int main() { //定义Student类型变量one和ans //one用来记录每次输入的学生信息,ans记录最高分学生信息 Student one,ans; ans.total = 0; int n; cin>>n; for(int i=1;i<=n;i++){ //输入到one的name、chinese、math、english成员变量中 cin>>one.name>>one.chinese>>one.math>>one.english; //计算one的total成员变量 one.total = one.chinese+one.math+one.english; if(one.total>ans.total){ ans = one; //非常简单的直接赋值来记录最高分学生信息 } } cout<<ans.name<<" "<<ans.chinese<<" "<<ans.math<<" "<<ans.english<<endl; return 0; }
结构体是由一系列数据(可以是不同的数据类型)构成的数据集合,并且结构体提供了一种自定义数据类型的手段。定义结构体的一般格式如下:
struct 类型名称{ 数据类型1 成员变量1(或者成员变量列表); 数据类型2 成员变量2(或者成员变量列表); //...其它成员变量 };
结构体定义的是一种数据类型,所以可以像申明int、double、string类型的变量那样来申明结构体变量。此外,可以用结构体变量名.成员名
来使用结构体内部的成员变量,这些成员变量的使用方法和普通的变量一致,可以输入、输出、赋值、参加运算(上面例子中cin>>one.xm>>one.yw>>one.sx>>one.yy;
、one.zf = one.yw+one.sx+one.yy;
、one.zf>ans.zf
)。更神奇的是,相同类型的结构体变量之间可以直接赋值(上面例子中更新最高分学生信息时直接使用ans = one;
)。
其实结构体中的成员变量也可以是结构体类型,例如上面的例子可以将成绩信息定义为一个Score结构体类型,学生信息定义为一个Student结构体类型,Student结构体中用一个Score结构体类型成员变量记录所有成绩信息:
#include<iostream> #include<string> using namespace std; struct Score{ int chinese,math,english,total; }; struct Student{ string name; Score score; }; int main() { Student one,ans; ans.score.total = 0; int n; cin>>n; for(int i=1;i<=n;i++){ cin>>one.name>>one.score.chinese>>one.score.math>>one.score.english; one.score.total = one.score.chinese+one.score.math+one.score.english; if(one.score.total>ans.score.total){ ans = one; } } cout<<ans.name<<" "<<ans.score.chinese<<" "<<ans.score.math<<" "<<ans.score.english<<endl; return 0; }
二、结构体数组
问题:P5741 旗鼓相当的对手 - 加强版
分析:用结构体Student存储一位学生信息,用结构体Student数组a来存储所有学生信息。通过循环列举1~N-1的每位学生a[i],对于a[i]判断其与i+1~N的每位学生a[j]是否是“旗鼓相当的对手”即可(这样不会出现重复判断):
#include<iostream> #include<string> using namespace std; struct Student{ string name; int chinese,math,english,total; }; Student a[1001]; bool judge(int x,int y,int z){ //判断|x-y|<=z? return x-y<=z && y-x<=z; } bool check(Student a,Student b){ //判断a、b两位学生是否是“旗鼓相当的对手” return judge(a.chinese,b.chinese,5) && judge(a.math,b.math,5) && judge(a.english,b.english,5) && judge(a.total,b.total,10); } int main() { int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i].name>>a[i].chinese>>a[i].math>>a[i].english; a[i].total = a[i].chinese+a[i].math+a[i].english; } for(int i=1;i<n;i++){ //从第i+1位开始的每位学生a[j],判断a[i]与a[j] for(int j=i+1;j<=n;j++){ if(check(a[i],a[j])){ cout<<a[i].name<<" "<<a[j].name<<endl; } } } return 0; }
三、*结构体成员函数
结构体中除了成员变量外,还可以有成员函数,成员函数类似于函数,可以认为它是结构体的“内部函数”,看下面的例子:
#include<iostream> #include<string> using namespace std; struct Student{ string name; int chinese,math,english; //成员函数(输出学生姓名和三科成绩) void print(){ cout<<name<<" "<<chinese<<" "<<math<<" "<<english<<endl; } //有返回值的成员函数(计算三科成绩总分) int total(){ return chinese+math+english; } }; int main() { Student one; one.name = "Jerry"; one.chinese = 120; one.math = 138; one.english = 145; one.print(); //调用成员函数 cout<<one.total()<<endl; //调用成员函数(有返回值) return 0; }
结构体有一种特殊的成员函数,其函数名与结构体名称相同,称为构造函数,可以用来初始化结构体:
struct Student{ string name; int chinese,math,english; Student(){} //没有任何参数的(默认)构造函数 //带参数的构造函数(参数名和成员名不同,特意加了一个_) Student(string _name,int _chinese,int _math,int _english){ this->name = _name; //可以简写成 name = _name; name是结构体成员,_name是参数 this->chinese = _chinese; this->math = _math; this->english = _english; } }; //注意比较和上面定义结构体的不同(带参构造函数的名称) struct Student{ string name; int chinese,math,english; Student(){} //没有任何参数的(默认)构造函数 //带参数的构造函数(参数名和成员名相同) Student(string name,int chinese,int math,int english){ this->name = name; //不能简写成 name = name;因为参数(局部变量)name将同名的成员name屏蔽了 this->chinese = chinese; this->math = math; this->english = english; } };
上面代码定义的结构体Student中有两个构造函数,一个是没有任何参数的Student();
另外一个是有参数的Student(string _name,int _chinese,int _math,int _english);
有了构造函数,就可以用构造函数来初始化结构体:
//原始的初始化结构体变量方法 Student s; s.name= "Jerry"; s.chinese = 120; s.math = 135; s.english = 150; //借助结构体构造函数初始化后赋值给结构体变量 Student ss = Student("Jerry",120,135,150); //使用结构体的构造函数直接初始化结构体变量 Student sss("Jerry",120,135,150);
下面来看成员函数的示例程序:
#include<iostream> #include<string> using namespace std; struct Student{ string name; int chinese,math,english; Student(){} //没有任何参数的(默认)构造函数 Student(string _name,int _chinese,int _math,int _english){ this->name = _name; //可以简写成 name = _name; this->chinese = _chinese; this->math = _math; this->english = _english; } int total(){ return chinese+math+english; } void print(){ cout<<name<<" "<<chinese<<" "<<math<<" "<<english<<endl; } }; int main() { //使用结构体的构造函数初始化变量 Student s("Jerry",120,135,150); cout<<s.total()<<endl; //调用结构体成员方法 s.print(); //调用结构体成员方法 return 0; }
通过上面的例子,我们应该能够体会到前面使用string类型变量的length()方法获取字符串长度(string s;cin>>s;cout<<s.length();
),其实string是一个类(和结构体相似,但功能比结构体更强),length是其中的成员函数(又称为方法)。
C++还支持更简洁的结构体初始化语句:
#include<iostream> #include<string> using namespace std; struct Student{ string name; int chinese,math,english; }; int main() { //更简洁的结构体初始化语句 Student s{"Jerry",120,135,150}; cout<<s.name<<" "<<s.chinese<<" "<<s.math<<" "<<s.english<<endl; return 0; }
借助成员方法,上一个问题“P5741 旗鼓相当的对手 - 加强版”还可以如下求解:
#include<iostream> #include<string> using namespace std; struct Student{ string name; int chinese,math,english; Student(){} //没有任何参数的(默认)构造函数 Student(string _name,int _chinese,int _math,int _english){ this->name = _name; //可以简写成 name = _name; this->chinese = _chinese; this->math = _math; this->english = _english; } int total(){ //计算返回总分 return chinese+math+english; } bool judge(int x,int y,int z){ //判断|x-y|<=z? return x-y<=z && y-x<=z; } bool check(Student a) { //判断当前学生与学生a是否是“旗鼓相当的对手” return judge(chinese,a.chinese,5) && judge(math,a.math,5) && judge(english,a.english,5) && judge(total(),a.total(),10); } }; Student a[1001]; int main() { int n; cin>>n; for(int i=1;i<=n;i++){ string name; int chinese,math,english; cin>>name>>chinese>>math>>english; a[i] = Student(name,chinese,math,english); } for(int i=1;i<n;i++){ //从第i+1位开始的每位学生a[j],判断a[i]与a[j] for(int j=i+1;j<=n;j++){ if(a[i].check(a[j])){ cout<<a[i].name<<" "<<a[j].name<<endl; } } } return 0; }
四、*重载输入输出运算符输入输出结构体变量
上面的程序输入并存储到结构体变量的语句仍然比较繁琐,可以重载输入运算符实现直接输入成员数据存储到结构体变量中,同样地还可以重载输出运算符实现直接输出结构体变量。
#include<iostream> #include<string> using namespace std; struct Student{ string name; int chinese,math,english; Student(){} //没有任何参数的(默认)构造函数 Student(string _name,int _chinese,int _math,int _english){ this->name = _name; //可以简写成 name = _name; this->chinese = _chinese; this->math = _math; this->english = _english; } int total(){ //计算返回总分 return chinese+math+english; } bool judge(int x,int y,int z){ //判断|x-y|<=z? return x-y<=z && y-x<=z; } bool check(Student a) { //判断当前学生与学生a是否是“旗鼓相当的对手” return judge(chinese,a.chinese,5) && judge(math,a.math,5) && judge(english,a.english,5) && judge(total(),a.total(),10); } }; Student a[1001]; //重载输入运算符 istream &operator>>(istream &in, Student &s){ in>>s.name>>s.chinese>>s.math>>s.english; return in; } //重载输出运算符 ostream &operator<<(ostream &os,Student &s){ //os<<s.name<<" "<<s.chinese<<" "<<s.math<<" "<<s.english<<" "<<s.total()<<endl; os<<s.name; return os; } int main() { int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; //因为重载了输入运算符,这里可以直接用cin>>a[i]; } for(int i=1;i<n;i++){ //从第i+1位开始的每位学生a[j],判断a[i]与a[j] for(int j=i+1;j<=n;j++){ if(a[i].check(a[j])){ cout<<a[i]<<" "<<a[j]<<endl; //因为重载了输出运算符,这里可以直接用cout<<a[i]; } } } return 0; }