注意:本文中代码均使用 Qt 开发编译环境
类的继承与派生?
以原有的类为基础产生新的类,我们就说新类继承了原有类的特征,也可以说从原有类派生出新类。
类的继承与派生好处?
提高了代码的重用性和可扩展性。
派生新类的过程一般包括?
吸收已有类的成员、调整已有类的成员和添加新的成员三个步骤。
在C++中,派生类的一般定义语法为?
class 派生类名:继承方式 基类名1,继承方式 基类名2,...,继承方式 基类名n
{
派生类成员声明; //是指除了从基类继承来的所有成员之外,新增加的数据和函数成员
};
一个派生类可以同时有多个基类,这种情况称为多继承。一个派生类只有一个基类的情况,成为单继承。
单继承可以看作是多继承的一个最简单的特例,多继承可以看成是单继承的组合。
例子:
class employee {
protected:
char name[20]; // 姓名
int individualEmpNo; // 个人编号
int grade; // 级别
float accumPay; // 月薪总额
static int employeeNo; // 本公司职员编号目前最大值
public:
employee();
~employee();
void pay(); // 计算月薪函数
void promote(int); // 升级函数
void SetName(char *); // 设置姓名
char * GetName(); // 提取姓名
int GetindividualEmpNo(); // 提取编号
int Getgrade(); // 提取级别
float GetaccumPay(); // 提取月薪
};
class technician:public employee { // 兼职技术人员类
private:
float hourlyRate; // 每小时酬金 派生类新增
int workHours; // 当月工作时数 派生类新增
public:
technician();
void SetworkHours(int wh); // 设置工作时数 派生类新增
void pay(); // 计算月薪函数 同名隐藏 隐藏基类
};
派生类过程:
1.吸收基类成员
这样,派生类实际上就包含了基类中除构造函数和析构函数之外的所有成员。在派生类的过程中构造函数和析构函数都不被继承。
2.改造基类成员
包括两个方面:一个是基类成员的访问控制问题,主要依靠派生类定义时的继承方式来控制;第二个是对基类数据或函数成员的隐藏,就是在派生类中声明一个和基类数据或函数同名的成员,例如例子中的pay()。此时使用成员名就只能访问到派生类中声明的同名成员,这称作同名隐藏。
3.添加新的成员
派生类新成员的加入是继承与派生机制的核心,是保证派生类在功能上有所发展的关键。
访问控制###
类的继承方式有public、protected、private三种,不同的继承方式,导致原来具有不同访问属性的基类成员在派生类中的访问属性也有所不同。
这里所说的访问来自两个方面:
一是派生类中新增的成员访问从基类继承的成员;
二是在派生类外部(非类族内的成员),通过派生类的对象访问从基类继承的成员。
<public>
当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。
解释:
基类的公有成员和保护成员被继承到派生类中访问属性不变,仍作为派生类的公有成员和保护成员,派生类的其他成员可以直接访问他们。
在类族之外只能通过派生类的对象访问从基类继承的公有成员,而无论是派生类的成员还是派生类的对象都无法直接访问基类的私有成员。
示例:
// rectangle.h 文件
#ifndef RECTANGLE_H
#define RECTANGLE_H
class Point {
public:
void InitP(float xx = 0, float yy = 0) {
X = xx;
Y = yy;
}
void Move(float xOff,float yOff) {
X += xOff;
Y += yOff;
}
float GetX() { return X; }
float GetY() { return Y; }
private:
float X,Y;
};
class Rectangle : public Point {
public:
void InitR(float x,float y,float w,float h) {
InitP(x,y);
W = w;
H = h;
}
float GetH(){return H;}
float getW(){return W;}
private:
float W,H;
};
#endif // RECTANGLE_H
// .cpp 部分
#include <QCoreApplication>
#include <QDebug>
#include "rectangle.h"
int main()
{
Rectangle rect;
rect.InitR(2,3,20,10);
rect.Move(3,2);
qDebug() << "The data of rect(X,Y,W,H):"
<< rect.GetX() << "," << rect.GetY() << ","
<< rect.getW() << "," << rect.GetH();
return 0;
}
运行结果:
<private>
当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可以直接访问。
解释:
基类的公有和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类族外部通过派生类的对象无法直接访问它们。
无论是派生类的成员还是通过派生类的对象,都无法直接访问从基类继承的私有成员。这也就是说,该种继承方式,阻止了基类功能的继续派生。
示例:
//Rectangle.h
#ifndef RECTANGLE_H
#define RECTANGLE_H
class Point {
public:
void InitP(float xx = 0, float yy = 0) {
X = xx;
Y = yy;
}
void Move(float xOff,float yOff) {
X += xOff;
Y += yOff;
}
float GetX() { return X; }
float GetY() { return Y; }
private:
float X,Y;
};
class Rectangle : private Point {
public:
void InitR(float x,float y,float w,float h) {
InitP(x,y);
W = w;
H = h;
}
void Move(float xOff,float yOff){
Point::Move(xOff,yOff);
}
float GetX(){return Point::GetX();}
float GetY(){return Point::GetY();}
float GetH(){return H;}
float getW(){return W;}
private:
float W,H;
};
#endif // RECTANGLE_H
// .cpp 部分与上一个示例中的相同
1.派生类的成员函数及对象无法直接访问积累的私有成员(例如X,Y)。
2.派生类的成员仍可以访问到从基类继承过来的公有和保护成员(例如在派生类函数成员InitR中直接调用基类的函数InitP)。
3.但是在类外部通过派生类的对象根本无法直接访问到积累的任何成员,积累原有的外部接口(例如基类的GetX()和GetY()函数)被派生类封装和隐藏起来。
在私有继承的情况下,为了保证基类的一部分外部接口特征能够在派生类中也存在,就必须在派生类中重新声明同名的成员。这里在派生类Rectangle中,重新声明了Move,GetX()和GetY()等函数,利用派生类对基类成员的访问能力,把基类的原有成员函数的功能照搬过来。
这种在派生类中重新声明的成员函数具有比基类同名成员函数更小的作用域,因此在调用时,根据同名隐藏的原则,自然会调用派生类的函数。
<protected>
保护继承中,基类的公有和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。
解释:
派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但是在类外部通过派生类的对象无法直接访问它们。
无论是派生类的成员还是通过派生类的对象,都无法直接访问从基类继承的私有成员。
基类中的保护成员有可能被它的派生类访问,但是绝不可能被其他外部使用者(比如程序中的普通函数、与基类平行的其它类等)访问。
示例1:
class A {
protected:
int x;
};
int main()
{
A a;
a.x = 5; // 错误!
return 0;
}
在A类对象a的模块中是无法访问A类的保护成员的,这种情况下,保护成员和私有成员一样得到了很好的隐藏。
示例2:
如果A类以公有方式派生产生了B类,则在B类中,A类保护成员和该类的公有成员一样是可以访问的。例如:
class A {
protected:
int x;
};
class B : public A {
public:
void function();
};
void B::function() {
x = 5; // 正确
}
int main()
{
B b;
b.function();
return 0;
}