类这个概念是面向对象思想在C++中的具体体现:它既是封装的结果,同时也是继承和多态的载体。所以,要想学习C++中的面向对象程序设计,也就必须从“类”开始。程序员
面向对象思想把现实世界中的全部事物都当作是对象,而类是对全部相同类型对象的抽象,是对它们整体的一个描述。好比,学校有不少老师,张老师、李老师、王老师,虽然每一个老师各不相同,是不一样的对象个体。但他们都是老师这一类型的对象,有着共同的属性(都有姓名、职务)和相同的行为(都能上课、批改做业)。咱们把某一类型对象的共同属性和相同行为抽象出来,分别用变量和函数加以描述,而后把这些变量和函数用类这个概念封装起来,就成了能够用来描述这一类对象的新的数据类型,这种新的数据类型所以也被称为类。在C++中,声明一个类的语法格式以下:函数
class 类名 { public : // 公有成员,一般用来描述这类对象的相同行为 protected: // 保护型成员 private: // 私有成员,一般用来描述这类对象的共同属性 }; // 注意这里有个分号表示类的结束
其中,class是C++中用以声明类的关键字,其后跟所要声明的类的名字,一般是某个能够归纳这一类对象的名词。其命名规则相似于以前介绍的变量名命名规则。这里,咱们要定义一个类来描述“老师”这类对象,因此咱们用“Teacher”做为这个类的名字。学习
在后面的章节中,咱们还将学到C++中的类还有基类与派生类之分,它是面向对象思想的继承机制在类当中的体现。若是这个类是从某个基类继承而来的,咱们在“class 类名”后面还要加上这个类的继承方式(public、protected或private)以及它所继承的基类的名字。这样,声明一个类的语法格式相应地就变为:spa
class 类名:继承方式 基类名 { // 成员变量和成员函数的声明… };
若是某个类没有继承关系,则类声明中的继承方式能够省略。这里的Teacher类自己就是基类,并非由其余类继承而来,因此这里继承方式应当省略。设计
完成类的名字及继承关系的定义后,就能够开始在类的主体中描述这个类的属性和行为了。对象的属性属于数据,因此咱们在类声明中定义一些变量来描述对象的属性。好比,“老师”这类对象拥有姓名这个属性,因此咱们就能够定义一个string类型的变量strName来描述。这些变量描述了对象的属性,成为了这个类总体的一部分,因此也被称为成员变量。code
最佳实践:在类声明中给成员变量初始值对象
若是类的某些成员变量具备初始值,咱们能够在类中声明这些成员变量的同时给它一个初始值,这样在运行期间,类就能够在进入构造函数以前,直接使用这个初始值完成相应成员变量的初始化。例如:blog
class Teacher { // 具备初始值的成员变量 protected: // 用字符串常量“Name”做为成员变量m_strName的初始值 string m_strName = “Name”; // 姓名 private: // 用常数2000做为成员变量m_unBaseSalary的初始值 unsigned int m_unBaseSalary = 2000; };
在这段代码中,咱们用两个常量分别做为类的两个成员变量m_strName和m_unSalary的初始值。通过这样的声明以后,在建立这个类的对象时,无需在构造函数中进行额外的初始化,它的这两个成员变量就会拥有相应的初始值。这一特性能够用于那些全部类的对象都拥有相同初始值的状况,好比全部“Teacher”对象的“m_unBaseSalary”(基本工资)都是2000元。继承
除了声明成员变量来描述对象的属性以外,对象的另一个重要组成部分就是它的行为。在C++中,咱们用函数来描述一个行为动做。一样,咱们也将函数引入类中成为它的成员函数,用来描述类对象的行为。好比,一个“老师”对象有备课的行为动做,咱们就能够为老师这个类添加一个PrepareLesson()函数,在这个函数中能够对老师备课动做进行具体的定义。类的构成如图6-7所示。接口
图6-7 类的构成
最佳实践:为类设计对程序员友好的接口
咱们所设计的类不只供咱们本身使用,更多时候它还会提供给其余程序员使用,以达到代码复用或者实现团队协做的目的。这时,类的接口设计的好坏,将会影响到他人可否正确并轻松地使用咱们所设计的类。所以,它也成为了衡量一个程序员水平高低的重要标准。
类的接口,就像类的使用说明书同样,是向类的使用者说明它所须要的资源及它所可以提供的服务等。只要类的接口对程序员友好,从接口就能够轻松地知道如何正确地使用这个类。要作到这一点,应当遵照下面这些设计原则。
l 遵循变量与函数的命名规则
成员变量也是变量,成员函数也是函数。因此,做为类的接口的它们,在命名的时候也一样应该遵照广泛的命名规则,让它们的名字可以准确而简洁地表达它们的含义。
l 简化类的视图
接口,表明了类所可以向用户提供的服务。因此,在进行类的接口设计时,只须要将必要的成员函数公有(public)就能够了,使用受保护的(protected)或者私有的(private)成员来向用户隐藏没必要要的细节。由于隐藏了用户不该该访问的内容,天然也就减小了用户犯错误的机会。
l 使用用户的词汇
类设计出来最终是让用户使用的,因此在设计类的接口时,应该从用户的角度出发,使用用户所熟悉的词汇,让用户在阅读类的接口时,不须要学习新的词汇或概念,这样能够平滑用户的学习曲线,让咱们的类使用起来更容易。
除了在类中定义变量和函数来表示类的属性和行为以外,还可使用public、protected及private这三个关键字来对这些成员进行修饰,指定它们的访问级别。按照访问级别的不一样,类的全部成员被分红了三个部分。一般,使用public修饰的成员外界能够访问,咱们会在public部分定义类的行为,提供公共的函数接口供外部访问;使用protected修饰的成员只有类本身和它的派生类能够访问,因此在protected部分,咱们能够定义遗传给下一代子类的属性和行为;最后private修饰的成员只有类本身能够访问,因此在private部分,咱们能够定义这个类所私有的属性和行为。关于类的继承方式和访问控制,稍后将进行详细介绍。这里先来看一个实际的例子。例如,要定义一个类来描述老师这一类对象,经过对这类对象的抽象,咱们发现老师这类对象拥有只有本身和子类能够访问的姓名属性和你们均可以访问的上课行为。固然,老师还有不少其余属性和行为,这里根据须要做了简化。最后,咱们使用面向对象的封装机制,将这些属性和行为捆绑到一块儿,就有了老师这个类的声明。
// 老师 class Teacher { // 成员函数 // 描述对象的行为 public: // 公有部分,供外界访问 void GiveLesson(); // 上课 // 成员变量 // 描述对象的属性 protected:// 受保护部分,本身和子类访问 string m_strName; // 姓名 private: };
经过这段代码,咱们声明了一个Teacher类,它是全部老师这种对象的一个抽象描述。这个类有一个public关键字修饰的成员函数GiveLesson(),它表明老师这类对象拥有你们均可以访问的行为——上课。它还有一个protected关键字修饰的变量m_strName,表示老师这类对象的只有它本身和子类能够访问的属性——姓名。这样,经过在一个类中声明函数描述对象的行为,声明变量描述对象的属性,就完整地声明了一个能够用于描述某类对象的类。
完成类的声明以后,咱们还须要对类的行为进行具体的定义。类成员函数的具体定义能够直接在类中声明成员函数的时候同时完成:
class Teacher { // 成员函数 // 描述对象的行为 public: // 声明成员函数的同时完成其定义 void GiveLesson() { cout<<"老师上课。"<<endl; }; //… };
更多时候,咱们只是将类的声明放在头文件(好比,Teacher.h文件)中,而将成员函数的具体实现放在类的外部定义,也就是相应的源文件(好比,Teacher.cpp)中。在类的外部定义类的成员函数时,咱们须要在源文件中引入类声明所在的头文件,而且在函数名以前还要用“::”域操做符指出这个函数所属的类。例如:
#ifndef _TEACHER_H // 定义头文件宏,防止头文件被重复引入 #define _TEACHER_H // 在稍后的7.3.1小节中会有详细介绍 // Teacher.h 类的声明文件 class Teacher { // … public: void GiveLesson(); // 只声明,不定义 }; #endif // Teacher.cpp 类的定义文件 // 引入类声明所在的头文件 #include "Teacher.h" // 在Teacher类外完成成员函数的定义 void Teacher::GiveLesson() { cout<<"老师上课。"<<endl; }
这里能够看到,成员函数的定义跟普通函数并没有二致,一样都是用函数来完成某个动做,只是成员函数所表示的是某类对象的动做。例如,这里只是输出一个字符串表示老师上课的动做。固然,在实际应用中,类成员函数还能够对成员变量进行访问,所完成的动做也要比这复杂得多。
知道更多:C++中用以声明类的另外一个关键字——struct
在C++中,要声明一个类,除了使用正牌的“class”关键字以外,以前在3.8节中介绍过的用来定义结构体的“struct”关键字也一样能够用来声明一个类。在语法上,“class”和“struct”很是类似,二者均可以用来声明类,而二者惟一的区别就是,在没有指定访问级别的默认状况下,用“class”声明的类当中的成员是私有的(private),而用“struct”声明的类当中的成员是公有的(public)。例如:
// 使用“struct”定义一个Rect类 struct Rect { // 没有访问权限说明 // 类的成员函数,默认状况下是公有的(public) int GetArea() { return m_nW * m_nH; } // 类的成员变量,默认状况下也是公有的(public) int m_nW; int m_nH; };
这里,咱们使用“struct”声明了一个Rect类,由于没有使用public等关键字显式地指明类成员的访问控制,在默认状况下,类成员都是公有的,因此能够直接访问。例如:
Rect rect; // 直接访问成员变量 rect.m_nH = 3; rect.m_nW = 4; // 直接访问成员函数 cout<<"Rect的面积是:"<<rect.GetArea()<<endl;
这两个关键字的默认访问控制要么过于保守,要么过于开放,这种“一刀切”的方式显然没法适应于全部状况。因此不管是使用“class”仍是“struct”声明一个类,咱们都应该在声明中明确指出各个成员的合适的访问级别,而不该该依赖于关键字的默认行为。
“class”和“struct”除了上面这点在类成员默认访问级别上的差别以外,从“感受”上讲,大多数程序员都认为它们仍有差别:“struct”仅像一堆缺少封装的开放的内存位,更多时候它是用以表示比较复杂的数据;而“class”更像活的而且可靠的现实实体,它能够提供服务、有牢固的封装机制和定义良好的接口。既然你们都这么“感受”,那么仅仅在类只有不多的方法而且有较多公有数据时,才使用“struct”关键字来声明类;不然,使用“class”关键字更合适。