数据类型:布尔型
定义常量名:#define —> const
const int LENGTH = 10;
输入输出函数:cin>> 和cout<< 换行:endl
类的声明、类成员的访问控制、类的成员函数及对象、对象指针:
函数只是语句和数据的封装,而类是函数与数据的封装其实,类就是一种自定义数据类型,跟一般的类型如int、char等有很多相似之处。我们可以定义int型的变量,同样也可以定义某个类类型的变量,用类定义的变量叫做类的对象,这种定义对象的过程叫做实例化。
类声明格式:
class 类名称
{
public:
公有成员(外部接口)
protected:
保护型成员
private:
私有成员
}
函数原型的声明要写在类主体中,原型说明了函数的参数类型和个数及返回值类型。而函数的具体实现是类声明之外的,下面是在类外写函数实现的方式:
返回值类型 类名:: 函数成员名(参数表)
{
函数体
}
声明一个对象和声明变量的方式是一样的:
类名 对象名;
比如,声明一个时钟类的对象:
Clock myClock;
声明了对象后就可以访问对象的公有成员,这种访问需要采用“.”操作符,调用公有成员函数的一般形式是:对象名.公有成员函数名(参数表)
,访问公有数据成员的形式是:对象名.公有数据成员
。
声明对象指针的方式与一般的指针类似:
类名 *对象指针名;
使用对象名可以方便的访问对象的公有成员,同样使用对象指针也可以很容易的访问对象的公有成员。用对象指针访问对象的公有成员的形式为:
对象指针名->公有成员名;
构造函数和析构函数
构造函数的作用就是在对象被创建时利用特定的初始值构造对象,把对象置于某一个初始状态,它在对象被创建的时候由系统自动调用,我们只需要使用默认的构造函数或者自己定义构造函数,而不用管怎么调用的。
构造函数也是类的成员函数,除了有成员函数的所有特征外,还有一些不同之处:构造函数的函数名跟类名一样,而且没有返回值。构造函数一般被声明为公有函数。
析构函数和构造函数的作用是相反的,它会在对象被删除之前做一些清理工作。析构函数是在对象要被删除时由系统自动调用的,它执行完后对象就消失了,分配的内存空间也释放了。
析构函数是类的一个公有函数成员,它的名称是在类名前加“~”形成,不能有返回值。一般如果我们想在对象被删除之前做什么工作就可以把它写到析构函数里。
class Clock
{
public:
Clock(int NewH, int NewM, int NewS); //构造函数
void SetTime(int NewH, int NewM, int NewS);
private:
int Hour, Minute, Second;
};
构造函数的实现:
Clock::Clock(int NewH, int NewM, int NewS)
{
Hour=NewH;
Minute=NewM;
Second=NewS;
}
析构函数的实现:
Clock::~Clock()
{
}
建立对象时构造函数的作用:
int main()
{
Clock c(0,0,0); //隐含调用构造函数,将初始值作为实参。
c.ShowTime();
return 0;
}
重载函数
重载函数就是,两个以上的函数取相同的函数名,但是函数形参的个数或者类型不同,编译器会根据实参与形参的类型和个数进行最佳匹配,自动确定调用哪一个函数。
友元
友元提供了一种不同类或对象的成员函数之间、类的成员函数与普通函数之间共享数据的机制。在一个类中声明友元的方式是,用关键字friend把普通函数、其他类的成员函数或其他类声明为此类的友元,用friend声明的元素就可以访问此类的私有数据。如果友元是普通函数或是类的成员函数就叫做友元函数,如果友元是一个类则叫做友元类,友元类的所有成员函数均为友元函数。
注意,友元关系不能传递,如果类B是类A的友元,类C又是类B的友元,类C和类A如果没有声明则没有友元关系,另外,友元关系是单向的,如果类B是类A的友元,类B的成员函数可以访问类A对象的私有成员和保护成员,但是类A的成员函数不能访问类B对象的私有成员和保护成员。
类的派生和继承
派生类声明的语法形式为:
class 派生类名 : 继承方式1 基类名1, 继承方式2 基类名2, ... 继承方式n 基类名n
{
派生类成员的声明;
}
例如,类Parent1和Parent2是已经声明过的类,类Child是由这两个类派生出的子类,则声明Child类的基本形式为:
class Child : public Parent1, private Parent2
{
public:
Child();
~Child();
}
在使用上一讲中讲到的声明方式声明派生类之后,再实现派生类的成员函数,这样派生类就算完整了。
根据前面所讲,派生类从基类继承的过程可以分为三个步骤:吸收基类成员(将基类成员中除了构造函数和析构函数外的所有其他成员全部接收)、修改基类成员(一种是通过设置派生类声明中的继承方式,来改变从基类继承的成员的访问属性;第二种是通过在派生类中声明和基类中数据或函数同名的成员,覆盖基类的相应数据或函数,注意函数形参表必须相同。)和添加新成员。
派生类的继承方式
第二个步骤中修改对基类成员的访问属性可以通过派生类的继承方式控制。类的继承方式也有public(公有继承)、protected(保护继承)和private(私有继承)三种。
派生类对基类成员的访问主要有两种,一种是派生类的新增成员对继承的基类成员的访问,另一种是派生类的对象对继承的基类成员的访问。
派生类的继承方式为public,即公有继承时,对基类中的公有成员和保护成员的访问属性都不变,而对基类的私有成员则不能访问。具体说,就是基类的公有成员和保护成员被继承到派生类中以后同样成为派生类的公有成员和保护成员,派生类中新增成员对他们可以直接访问,派生类的对象只能访问继承的基类公有成员。
在protected继承方式中,基类的公有成员和保护成员被派生类继承后变成派生类的保护成员,而基类的私有成员在派生类中不能访问。因为基类的公有成员和保护成员在派生类中都成了保护成员,所以派生类的新增成员可以直接访问基类的公有成员和保护成员,而派生类的对象不能访问它们,上一讲鸡啄米说过,类的对象也是处于类外的,不能访问类的保护成员。对基类的私有成员,派生类的新增成员函数和派生类对象都不能访问。
在private继承方式中,基类的公有成员和保护成员被派生类继承后变成派生类的私有成员,而基类的私有成员在派生类中不能访问。派生类的新增成员可以直接访问基类的公有成员和保护成员,但是在类的外部通过派生类的对象不能访问它们。而派生类的成员和派生类的对象都不能访问基类的私有成员。
派生类的构造函数:
对派生类初始化时就需要对基类的数据成员、派生类新增数据成员和内嵌的其他类对象的数据成员进行初始化。由于不能继承基类的构造函数,派生类就必须增加自己的构造函数。派生类的构造函数需要做的工作有,使用传递给派生类的参数,调用基类的构造函数和内嵌对象成员的构造函数来初始化它们的数据成员,再添加新语句初始化派生类新成员。
派生类构造函数的语法形式为:
派生类名::派生类名(参数表):基类名1(参数表1),...基类名m(参数名m),内嵌对象名(内嵌对象参数表1),...,内嵌对象名n(内嵌对象参数表n)
{
初始化派生类新成员的语句
}
例如在派生类声明中定义构造函数:
Child(int i,int j,int k,int m):Base2(i),b3(j),b2(k),Base3(m) { }
在main()中调用:
Child child(3,4,5,6);
派生类的析构函数:
派生类的析构函数一般只需要在其函数体中清理新增成员就可以了,对于继承的基类成员和派生类内嵌对象成员的清理,则一般由系统自动调用基类和对象成员的析构函数来完成。生类析构函数的定义方式与一般类的析构函数是一样的,也是没有返回类型,没有参数。
作用域分辨符:
如果在派生类中存在一个和基类某数据成员同名的数据成员,或者和基类某成员函数的名称和参数表都相同的成员函数,则派生类中的新成员就覆盖了基类成员,不管是在派生类内还是在派生类外部只能通过成员名访问到派生类的成员,而访问不到基类成员。如果需要在派生类中访问基类中的同名成员怎么办呢?
我们可以通过基类名和作用域分辨符来访问基类中的同名成员。作用域分辨符就是“::”,在派生类内部访问基类同名成员的语法形式是:
基类名::数据成员名; // 数据成员
基类名::函数成员名(参数表); // 函数成员
如果是在派生类外通过派生类对象访问的话,前面还要加上“派生类对象名.”:
派生类对象名.基类名::数据成员名; // 数据成员
派生类对象名.基类名::函数成员名(参数表); // 函数成员
如果派生类中不存在同名成员,访问多个基类的同名成员也需要使用作用域分辨符。如果派生类的全部或者部分基类有共同的基类,也就是说派生类的这些基类是从同一个基类派生出的,那么派生类的这些直接基类从上一级基类继承的成员都具有相同的名称,即都是同名成员,要访问它们就必须通过直接基类限定,使用作用域分辨符访问。
C++程序的多文件结构
一个程序按结构至少可以划分为三个文件:类的声明文件(*.h文件)、类的成员函数的实现文件(*.cpp文件)和主函数文件(使用到类的文件),如果程序更复杂,我们会为每个类单独建一个声明文件和一个实现文件。这样我们要修改某个类时就直接找到它的文件修改即可,不需要其他的文件改动。