首先,竞赛中并不推荐自定义“类”来解决问题,但是我们要使用本章节介绍的标准模板库(STL),需要了解一些类的基础知识。如果只是简单地使用类,那么类的使用方法和之前介绍的结构体类似。
前面介绍过结构体,我们已经知道了结构体是由一批数据组合而成的一种新的数据类型——结构体能够将一些不同类型的数据“捆绑”聚合成一个整体从而实现自定义复合型数据类型。C语言中结构体的功能较简单,C++中将结构体的功能进一步扩充(例如增加了构造函数、成员函数、重载运算符等功能)。C++还提供了一个更强大的面向对象的程序设计利器——类。
一、类
C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。
C++中类的基础使用方法和前面介绍的结构体相似(其实,类的使用方法相当复杂,这里只介绍最基础的),类中有成员变量,有成员函数,也有特殊的构造函数。
//Circle类 class Circle{ double r; //圆的半径r作为Circle类的成员变量 //默认无参数构造函数 Circle(){ this->r = 0; } //带参数构造函数 Circle(double r){ this->r = r; } //成员函数:计算返回圆周长 double length(){ return 2*3.14*this->r; } //成员函数:计算返回圆面积 double area(){ return 3.14*this->r*this->r; } };
细看上面的代码,会发现C++中类(简单实用是)和结构体的使用很相似,只不过定义类时,使用的是class
关键字。
二、访问修饰符
类有一套特殊的设置成员变量和成员函数的访问机制——对于类的成员变量和成员函数,需要指定其是公共的(public)或者是私有的(private)或者是受保护的(protected)。
来看一个简化的描述圆的类:
#include<iostream> using namespace std; //Circle类 class Circle{ double r; //圆的半径r作为Circle类的成员变量 }; int main() { Circle c; //定义Circle类型变量c return 0; }
C++中类和结构体的使用很相似,但是还有细节上的不同。这里尝试在 main 函数中,通过类似于结构体的“变量名.成员名”的形式来设置 Circle 类型变量c的成员变量r的值:
c.r = 12.56;
会出现编译错误,Dev C++中出现的编译错误提示是: 'double Circle::r' is private
,表明 Circle 类的成员变量 r 是 private(私有的),只能在类的内部(类的定义语句中)直接访问,不能在类的外部这样访问 r。我们修改一下 Circle 类的内容,将成员变量 r 设置成 public:
#include<iostream> using namespace std; //Circle类 class Circle{ public: //访问修饰符,public指明后面的成员是公共的 double r; //圆的半径r作为Circle类的成员变量 }; int main() { Circle c; //定义Circle类型变量c c.r = 12.56; cout<<c.r<<endl; return 0; }
现在成员变量 r 是public(公共的),在类的外部就可以通过 c.r
的形式来访问了。
上面的例子说明了简单使用类时,与结构体相比较,类额外有成员变量和成员函数的访问机制。如果没有指明成员的访问修饰符,那么默认为是 private(私有的),这也是程序最开始编译出错的原因。
三、类的使用举例
我们来定义一个功能丰富的描述圆的类Circle。成员变量很简单就是一个double r;
用来描述圆的半径,可以设置成private。设计两个构造函数用来初始化Circle变量,还设计有获取和设置圆半径r的成员函数、计算圆周长和面积的成员函数,最后还重载了一些运算符。
#include<iostream> using namespace std; //Circle类 class Circle{ private: double r; public: //(public冒号:后面的成员都是public) //默认构造函数 Circle(){ this->r = 0; } //一个参数的构造函数 Circle(double r){ this->r = r; } //成员函数:返回圆半径 double getR() const{ return this->r; } //成员函数:设置圆的半径 void setR(double r){ this->r = r; } //成员函数:计算返回圆周长 double length() const{ return 2*3.14*this->r; } //成员函数:计算返回圆面积 double area() const{ return 3.14*this->r*this->r; } }; //重载运算符 < bool operator < (const Circle &a,const Circle &b){ return a.getR()<b.getR(); } //重载运算符 > bool operator > (const Circle &a,const Circle &b){ return a.getR()>b.getR(); } //重载运算符 == bool operator == (const Circle &a,const Circle &b){ return a.getR()==b.getR(); } //重载运算符 <= bool operator <= (const Circle &a,const Circle &b){ //借助已经重载的<和==运算符 return a<b || a==b; } //重载运算符 >= bool operator >= (const Circle &a,const Circle &b){ //借助已经重载的>和==运算符 return a>b || a==b; } int main() { Circle c1(5.6),c2(7.8); //调用构造函数初始化Circle变量 //因为成员r是private,不能使用c1.r直接访问 //但是可以通过getR获取成员变量r的值 //详见上面getR成员函数中的语句:return this->r; cout<<c1.getR()<<endl; //调用length和area计算周长和面积 cout<<c1.length()<<endl; cout<<c1.area()<<endl; //通过重载运算符进行比较 if(c1<c2) cout<<"c1<c2"<<endl; //类中重载了运算符 < else if(c2>c1) cout<<"c2>c1"<<endl; //类中重载了运算符 > else if(c1==c2) cout<<"c1==c2"<<endl; //类中重载了运算符 == //调用成员函数设置半径 c2.setR(c1.getR()); if(c1==c2) cout<<"c1==c2"<<endl; //类中重载了运算符 == //new初始化Circle指针pc Circle *pc = new Circle(8.75); cout<<pc->getR()<<endl; cout<<pc->length()<<endl; cout<<pc->area()<<endl; return 0; }
四、再看string类
前面介绍字符串的时候提到,C++中使用了全新的数据类型string来处理字符串,其实string就是C++中内置的一个专门用于处理字符串的类(这个类定义语句在头文件string中,所以我们使用string时需要include这个头文件)。这里不讨论string的内部实现,我们通过string的用法来进一步认识类的使用。
string有丰富的构造函数:
#include<iostream> #include<string> using namespace std; int main() { string s; //调用默认构造函数,s为空字符串 string s1("Hello World"); //构造函数:初始化s1为字符串常量"Hello World" cout<<s1<<endl; string s2(s1); //构造函数:初始化s2为变量s1的值 cout<<s2<<endl; string s3(s1,4); //构造函数:初始化s3为s1的子串(从第4个字符开始取) cout<<s3<<endl; string s4(s1,4,3); //构造函数:初始化s4为s1的子串(从第4个字符开始取,一共3个) cout<<s4<<endl; char cstr[] = "Nice to meet you!"; //C语言用字符数组存储的字符串 string s5(cstr); //构造函数:初始化s5为字符数组存储的字符串 cout<<s5<<endl; string s6(cstr,6); //构造函数:初始化s6为字符数组存储的字符串的前6位子串 cout<<s6<<endl; string s7(12,'*'); //构造函数:s7初始化为12个'*'字符组成的字符串 cout<<s7<<endl; return 0; }
string有丰富的成员函数:
#include<iostream> #include<string> #include<cstring> using namespace std; int main() { string s1("Hello World"); string s2("Nice to meet you"); //length或者size获取字符串长度 cout<<s1.length()<<endl; cout<<s1.size()<<endl; //at获取指定位置的字符 cout<<s1.at(0)<<endl; //cout<<s1[0]<<endl; //c_str返回string对应的字符数组指针 cout<<s1.c_str()<<endl; char s[110]; strcpy(s,s1.c_str()); cout<<s<<endl; //find,rfind查找字符串(或字符)出现位置 //查找还有find_first_of、find_last_of、find_first_not_of、find_last_not_of等 cout<<s1.find("llo")<<endl; cout<<s1.rfind('l')<<endl; //append向字符串末尾追加内容 s1.append("!"); cout<<s1<<endl; //insert从指定位置插入另一个字符串 s1.insert(0,"Say "); cout<<s1<<endl; //replace将指定位置开始的指定长度的内容替换成另一个字符串 s1.replace(4,5,"hi"); cout<<s1<<endl; //erase清除指定位置开始的指定长度的内容 s1.erase(4,3); cout<<s1<<endl; //substr从指定位置获取制定长度的子串 cout<<s2.substr(8,4)<<endl; cout<<s2.substr(8)<<endl; //swap交换string的值 s1.swap(s2); cout<<s1<<"\t"<<s2<<endl; //clear清空字符串内容(成为空串),empty判断是否是空字符串 s1.clear(); cout<<s1.length()<<endl; if(s1.empty()) cout<<"s1 is empty string"<<endl; return 0; }
string中还重载了很多运算符:
#include<iostream> #include<string> using namespace std; int main() { //重载了赋值运算符,将字符串常量直接赋值给string变量 string s1 = "Hello World"; string s2 = "Nice to meet you"; //重载了[]运算符,通过下标方式访问字符串内部的字符(取值赋值均可) cout<<s1[0]<<endl; s1[0] = 'h'; cout<<s1<<endl; //重载了+运算符,实现了向字符串末尾追加内容(拼接) cout<<s1+s2<<endl; cout<<s1+'Y'<<endl; //重载了+=运算符,实现了自加效果 string ss = "Hello"; ss += " World!"; cout<<ss<<endl; //重载了一系列比较运算符 if(s1==s2) cout<<s1<<" == "<<s2<<endl; if(s1>s2) cout<<s1<<" > "<<s2<<endl; if(s1>=s2) cout<<s1<<" >= "<<s2<<endl; if(s2<s1) cout<<s2<<" < "<<s1<<endl; if(s2<=s1) cout<<s2<<" <= "<<s1<<endl; return 0; }