继承是面向对象程序设计的第二个重要特性,通过继承实现了数据抽象基础上的代码重用。
继承的作用是减少代码冗余,通过协调来减少接口和界面。
两种继承方式:
单一继承:派生类只能由一个直接基类。
多重继承:派生类可以有多个直接基类。
一. 继承与派生
派生类的特点:
1. 新的类可在基类的基础上包含新的成员
2.新的类中可隐藏基类的成员函数
3.可为心累重新定义成员函数
基类与派生类的关系如下:
1.派生类是基类的具体化
2.派生类是基类定义的延续
3.派生类是基类的组合
二. 派生类的定义
class <派生类名>:[继承方式]<基类名>
{
//派生类成员声明;
};
派生新类的过程:
1.吸收基类成员
C++语言的类继承,首先是基类成员的全盘吸收,这样派生类实际上就包含了所有基类的除构造函数和析构函数之外的所有成员。
2.改造基类成员
一是依靠派生类的继承方式来控制基类成员的访问。
二是对基类数据成员或成员函数的覆盖。即在派生类中定义一个和基类数据成员或成员函数同名的成员,由于作用域不同,产生成员覆盖,又叫同名覆盖,即当一个基类中声明的成员名又在派生类中重新声明所产生的效果,基类中的成员就被替换成派生类的同名成员。
3.添加新的成员
新的析构函数和构造函数
三. 类的继承方式
基类的成员可以有public、protected、private三种访问类型。默认private访问。
基类内部,自身成员可以对任何一个其他成员进行访问,但通过基类的对象,只能访问基类的公有成员。
派生类继承了基类的全部数据成员,除了构造函数、析构函数之外的全部成员函数,但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员,其访问属性由继承方式控制。
类的继承方式有public,protect,private继承三种。
访问属性 \继承方式 | public | protected | private |
public | public | protected | 不可访问的 |
protected | protected | protected | 不可访问的 |
private | private | private | 不可访问的 |
1. 公有继承
(1) 基类的公有成员相当于派生类的公有成员,即派生类可以像访问自身公有成员一样访问基类继承的共有成员
(2) 基类的保护成员相当于派生类的保护成员,即派生类可以像访问自身保护成员一样访问基类继承的保护成员
(3) 基类的私有成员,派生类内部成员无法直接访问。派生类使用者也无法通过派生类对象直接访问。
#include <iostream>
class vehicle
{
private:
float weight;
int wheels;
public:
vehicle(int in_wheels,float in_weight)
{wheels=in_wheels;weight=in_weight;}
int get_wheels(){return wheels;}
float get_weight(){return weight;}
};
class car:public vehicle
{
private:
int passenger_load;
public:
car(int in_wheels,float in_weight,int people=5):vehicle(int in_wheels,float in_weight)
{passenger_load=people;}
int get_passengers(){return passenge_load;}
};
void main()
{
car bm(4,1000);
cout<<"The message of bm: "<<endl;
cout<<bm.get_wheels()<<",";
cout<<bm.get_wheight()<<",";
cout<<bm.get_passengers()<<endl;
}
注:public继承,派生类成员函数及派生类对象可以访问基类的共有成员,但是无法访问基类的私有数据。
2.私有继承
派生类对基类的访问权限如下:
(1) 基类的公有成员和保护成员相当于派生类的私有成员,派生类只能通过自身的成员函数访问它。
(2) 基类的私有成员,无论是派生类内部成员或派生类的对象都无法直接访问。
#include <iostream>
class vehicle
{
private:
float weight;
int wheels;
public:
vehicle(int in_wheels,float in_weight)
{wheels=in_wheels;weight=in_weight;}
int get_wheels(){return wheels;}
float get_weight(){return weight;}
};
class car:private vehicle
{
private:
int passenger_load;
public:
car(int in_wheels,float in_weight,int people=5):vehicle(int in_wheels,float in_weight)
{passenger_load=people;}
int get_wheels(){return vehicle::wheels;} //重新定义,同名覆盖
float get_weight(){return vehicle::weight;}
int get_passengers(){return vehicle::passenge_load;}};
void main(){
car bm(4,1000);
cout<<"The message of bm: "<<endl;
cout<<bm.get_wheels()<<",";
cout<<bm.get_wheight()<<",";
cout<<bm.get_passengers()<<endl;
}
注:在私有继承的情况下,为了保证基类的部分外部接口特征保留在派生类中,就必须在派生类中重新定义同名的成员函数。
3. 保护继承
(1) 基类公有成员和保护成员相当于派生类的保护成员,派生类可以通过自身的成员函数或子类的成员函数访问他们。
(2) 基类的私有成员,无论是派生类内部成员或派生类的对象都无法直接访问。
#include <iostream>
class vehicle
{
private:
int wheels;
protected:
float weight;
public: vehicle(int in_wheels,float in_weight) {wheels=in_wheels;weight=in_weight;} int get_wheels(){return wheels;} float get_weight(){return weight;}};class car:private vehicle{ private: int passenger_load; public: car(int in_wheels,float in_weight,int people=5):vehicle(int in_wheels,float in_weight) {passenger_load=people;}
int get_wheels(){return vehicle::wheels;} //重新定义,同名覆盖
float get_weight(){return weight;}
int get_passengers(){return vehicle::passenge_load;}};
void main(){
car bm(4,1000);
cout<<"The message of bm: "<<endl;
cout<<bm.get_wheels()<<",";
cout<<bm.get_wheight()<<",";
cout<<bm.get_passengers()<<endl;
}
四. 派生类的构造函数和析构函数
1.派生类的构造函数
两种情况必须定义派生类的构造函数:
(1) 派生类本身需要构造函数
(2) 定义派生类对象时,其相应的基类对象需要调用带有参数的构造函数
初始化派生类对象,就要对基类数据成员、新增数据成员和对象成员的数据成员进行初始化。拍生路的构造函数需要以合适的初值作为参数,隐含调用基类和新增的内嵌对象成员的构造函数来初始化它们各自的数据成员,然后再加入新的语句对新增普通数据成员进行初始化。
派生类构造函数声明的一般语法形式如下:
派生类构造函数(参数表):基类构造函数(参数表),对象成员1(参数表),...对象成员n(参数表)
{拍生路新增成员的初始化语句;}
注:1. 派生类的构造函数名与派生类名相同
2. 参数表需要列出初始化基类数据、新增内嵌对象数据及新增一般数据成员所需要的全部参数。
3. 冒号后,列出需要使用参数进行初始化的基类名和内嵌成员名及各自的参数表。
在定义派生类对象时构造函数的执行顺序是先祖先(基类,调用顺序按照它们继承时说明的顺序),再客人(对象成员,调用顺序按照它们在类中说明的顺序),后自己(派生类本身)。
例:
#include<iostream>
class data
{
int x;
public:
data(int x)
{
data::x=x;
}
};
class a
{
data d1;
public:
a(intx):d1(x)
{cout<<"class a\n";}
};
class b:public a
{
data d2;
public:
b(int x):a(x),d2(x)
{cout<<"class b\n"}
};
class c:public b
{
public:
c(int x):b(x)
{cout<<"class c\n";}
};
void main()
{
c object(5);
}
运行结果:
class data
class a
class data
class b
class c
2.析构函数
派生类析构函数的定义方法与没有继承关系的类中析构函数的定义方法完全相同,只要在函数体中负责把派生类新增的非对象成员的数据成员的清理工作做好就够了,系统会自己调用基类及对象成员的析构函数来对基类及对象成员进行清理。
析构函数的执行顺序和析构函数正好严格相反:先自己(派生类本身),再客人(对象成员),后祖先(基类)。
例:
#include<iostream>
class person
{
char * name;
int age;
char add;
public:
person()
{cout<<"the constructor"}
};