第4章 C++的面向对象编程   C++是面向对象的程序设计语言,类是其实现面向对象的工具。类是面向对象程序设计(OOP)实现信息封装的基础,是C++语言最重要的特性。其是进行数据封装和数据保护的工具。本章讲解的知识点包括: * 类和对象的基本概念; * 友元; * 派生类; * 继承与多重继承; * 多态; * 函数重载和运算符重载; * 虚函数。   通过本章的学习,读者可以掌握C++面向对象方面的特点,了解C++中类的出现的基本理论,掌握类的定义和使用,掌握类的特性。 4.1 类和对象的基本概念   类是现实事物共有特征的抽象,而对象是类的具体实例。在面向对象的程序设计中,总是先声明类,再由类生成其对象。类是建立对象的模板,对象则是类的一个实例。 4.1.1 从结构到类   在C语言中有一种自定义的数据类型,即结构体。结构体中可以包含不同的数据类型。C++语言把结构体进行扩充,它不仅可以含有不同的数据类型,而且可以含有函数。在C++中结构体的定义形式如下:    struct 结构体名 { 成员表列 }变量名表列;      struct是定义结构体的关键字,后面的结构体名是结构体的名称。在结构体中可以定义一个或多个成员变量或函数。当然也可以不定义变量,此时称为空结构体。变量名表列是在定义结构体时,同时定义结构体变量。也可以不定义变量,在后面用到的地方再定义结构体变量。需要注意的是,结构体右括号的变量名表列后需要添加一个“;”。   【示例4.1】 声明一个结构体,并定义一个结构体变量A。    struct student //定义结构体 { //结构体成员 int math; int english; int chemistry; int all(int a,int b,int c) { return a+b+c; } }A; //定义结构体变量      分析:在这个声明的结构体中,含有3个整数,分别代表数学,英语和化学成绩。另外含有一个函数用于计算总成绩。并且定义了一个结构体变量A。此时可以使用“.”访问结构体的成员变量和成员函数。可以进行赋值,访问等操作。 ?注意:一个空的结构体的大小并不为0,空结构体的大小为1。   结构体中的数据和函数都是结构体的成员,分别叫做结构体的数据成员和函数成员。下面看一个完整的例子。   【示例4.2】 利用上面声明的结构体,输入3门课成绩,输出总成绩。    #include "iostream.h" struct student //定义结构体 { int math; int english; int chemistry; int all() //成员函数 { return math+english+chemistry; } }A; //定义结构体变量 void main() { int a;int b;int c; cin>>a>>b>>c; A.math=a; //结构体成员的访问 A.english=b; A.chemistry=c; cout< class number //声明类number { private: int num; public: void fun(int i); //类的成员函数 }; void number::fun(int i) { num=i; //访问类的私有成员 } void main() { int num=9; //定义变量 number A; A.num=3; //错误,类的私有成员 A.fun(3); num=10; //访问主函数中定义的变量num }   分析:上面的number类中定义了一个私有成员变量num,在main()函数中对该变量进行访问是错误的。因为超出了类的变量作用域。而在fun()函数中,虽然该函数的定义在类的外部实现,但它本身还是类的一部分。num仍然在作用域内。在后面主程序num赋值时,访问的是主函数中定义的变量,而不是类的成员变量。 4.2 构造函数和析构函数   类是一种用户自定义的数据类型。定义一个类的对象时,编译程序需要为对象分配存储空间,进行必要的初始化工作。在C++中类的构造函数就是去实现这部分功能。 4.2.1 构造函数   构造函数是一种特殊的成员函数,它主要进行一些初始化工作。构造函数有以下基本 性质。 * 构造函数和类的名字相同。 * 构造函数不具有返回值。 * 定义对象时,系统自动调用构造函数。 * 构造函数是公有函数,但它不可以被显式调用。   【示例4.10】 给出一个构造函数定义的例子,该类在其声明中定义了一个成员函数,函数名称与类名相同。并且该函数没有返回值,即定义了类的构造函数。    class point //声明类 { private: int x;int y; public: float distance() //类的成员函数 { return sqrt(x*x+y*y); } point(int a,int b) //类的构造函数 { x=a;y=b; } }A;      分析:上面的代码首先定义一个类,可以看到,仅仅把上例中的init函数名字换为构造函数的名字,即类名。这个函数就称为构造函数。   创建对象时,构造函数会自动被调用,不用显式调用。当程序员没有定义构造函数 时,编译系统会自动生成一个构造函数。构造函数可以带参数,也可以不带参数。当构造函数有参数时,创建对象必须指定构造函数的参数,否则,由于构造函数得不到实参,系统会报错。 4.2.2 默认参数的构造函数   对于有参数的构造函数,在定义对象时必须给构造函数传递参数。实际情况中,虽然有些构造函数有参数,但其参数是不变的,这就是默认参数的构造函数。此时构造函数的形式如下:    类名(函数名)(参数1=默认值,参数2=默认值,…)      构造函数中的参数与普通函数的参数是一样的,所谓的默认参数即为该参数设置一个默认的取值。可以为全部或者部分参数设置默认值。   【示例4.11】 利用前面的point类,用带默认参数的构造函数计算(2,3)、(7,3)、(4,6)到原点的距离。    #include"iostream.h" #include"math.h" //包含头文件 class point { private: int x;int y; public: float distance() //类的成员函数 { return sqrt(x*x+y*y); } point(int a=2,int b=3) //默认参数的构造函数 { x=a;y=b; } }; void main() { point A; //全部使用默认值 point B(7); //只取一个默认值 point C(4,6); //不取默认值 cout< class classexample { private: int i; double d; public: classexample() //构造函数1 { i=0;d=0.0; } classexample(int numi) //构造函数2 { i=numi; d=0.0; } classexample(int numi,double numd) //构造函数3 { i=numi; d=numd; } void print() { cout<<"i="<>x>>y; point pt; //定义类的对象 init(pt,x,y); //使用友元函数 cout<print(); //调用基类的成员函数 p=&opb; //将类B的对象赋给指针 p->print(); //调用基类的成员函数,而不是调用派生类的成员函数 }      程序输出结果如下:    基类输出 基类输出      这是因为函数在编译时是静态联编的。如果想调用派生类则可以采用显式调用,即利用派生类对象调用,必须采用虚函数。虚函数的定义形式如下:    virtual 函数类型 函数名(参数表) {}      把函数声明为虚函数,编译系统就可以进行动态联编,既可以正确调用派生类的成员函数。把上面基类的print函数声明为虚函数,代码如下:    virtual void print()      程序输出结果如下:    基类输出 派生类输出      通过上面的分析可以看到,利用虚函数可以实现对派生类的成员函数正确调用。   有时基类表示一个抽象的概念,它并不与任何实际事物联系,这就是抽象类。抽象类中的所有函数并不具有任何意义,但是可以为它的派生类打下基础。抽象类中声明的虚函数则称为纯虚函数。它在基类中没有定义,但它要求在派生类中去定义。 ?注意:抽象类中至少含有一个纯虚函数,而且抽象类不可以创建对象,只可以作为其派生类的基类。 4.10 本章实例——类的使用   前面介绍了继承、多态和重载的基本概念和使用方法。C++作为面向对象的程序设计语言,继承、多态和重载的出现使C++面向对象的特点体现的更加明确。本节给出几个例子。   【示例4.26】 创建复数类,利用运算符重载,实现其加法和减法。    #include class complex //定义一个复数类 { public: complex(double xx = 0.0, double yy = 0.0) //默认参数的构造函数 {x = xx;y = yy;} complex operator+(complex); //重载运算符 complex operator-(complex); void show(); private: double x; double y; }; complex complex::operator+(complex b) //重载运算符+ { complex a; a.x = x+b.x; a.y = y+b.y; return a; } complex complex::operator-(complex b) //重载运算符- { complex a; a.x = x-b.x; a.y = y-b.y; return a; } void complex::show() //成员函数 { if(y>=0) cout< #include class point { public: point(double a=0,double b=0){X = a;Y = b;} //构造函数 point(const point &p) //拷贝构造函数 { X = p.X;Y = p.Y; } ~point(){} double distance(point &p) //成员函数 { double dis; dis = sqrt(pow(this->X-p.X,2)+pow(this->Y-p.Y,2)); return dis; } void print() { cout<<"X= "<X<<" "<<"Y= "<Y<