继承和派生

继承和派生

定义

   继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。
派生类是通过对基类进行修改和扩充得到的,在派生类中,可以扩充新的成员变量和成员函数。派生类一经定义,可以独立使用,不依赖基类。
派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public。
在派生类的各个成员函数中,不能访问基类中的private成员。
一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CStudent{
private:
string sName;
int nAge;
public:
bool IsGood() {};
void SetName(const string&name){ sName = name;}
};
class CUndergraduateStudent: public CStudent{
private:
int nDepartment;
public:
bool IsGood() {};
bool CanBaoYan() {};
};

派生类对象的内存空间

派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。

继承关系和复合关系

类之间的两种关系

继承:“是”关系。
-基类A,B是基类A的派生类。
-逻辑上要求:“一个B对象也是一个A对象”
复合: “有”关系
-类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
-一般逻辑上要求:“D对象是C对象的固有属性或组成部分”

复合关系的使用

几何形体程序中,需要写“点”类,也需要写“圆”类,两者的关系就是复合关系—每一个“圆”对象里都包含(有)一个“点”对象,这个“点”对象就是圆心。

1
2
3
4
5
6
7
class Cpoint{
double x,y
};
class CCircle{
double r;
CPoint center;
};

派生类覆盖基类成员

派生类可以定义一个和基类成员同名的成员,这叫覆盖。在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class base{
int j;
public:
int i;
void func();
}
class derived:public base{
public:
int i;
void access;
void func();
};

void derived::access(){
i = 5;//引用的是派生类的i
base::i=5;//引用的是基类的i
func();
base::func();
}
derived obj;
obj.i = 1;
obj.base::i = 1;

类的保护成员

基类的private成员,可以被这些函数访问:基类的友元函数;基类的成员函数
基类的public成员,可以被这些函数访问:基类的成员函数,基类的友元函数,派生类的成员函数,派生类的友元函数,其他的函数
基类的protected成员,可以被这些函数访问:基类的成员函数,基类的友元函数,派生类的成员函数可以访问当前对象的基类的保护成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Father{
private:int nPrivate;
public:int nPublic;
protected:int nProtected;
};
class Son: public Father{
void AccessFather(){
nPublic =1;
nPrivate = 1;//wrong
nProtected = 1;
Son fl
f.nProtected = 1;
}
};
int main()
{
Father f;
Son s;
f.nPublic = 1;
s.nPublic = 1;
f.nProtected = 1;//error
return 0;
}

派生类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Bug{
private:
int nLegs; int nColors;
public:
int nType;
Bug(int legs, int color);
void PrintBug() { };
};
class FlyBug:public Bug{
int nWings;
public:
FlyBug(int legs,int color,int wings);
};
Bug::Bug(int legs,int color){
nLegs = legs;
nColor = color;
};
FlyBig::FlyBug(int legs,int color,int wings):Bug(legs,color){
nWings = wings;
}
int main(){
FlyBug fb(2,3,4);
fb.PrintBug();
fb.nType = 1;
fb.nLegs = 2;//error
return 0;
}

在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数时,总是先执行一个基类的构造函数。
调用基类构造函数的两种方式
显式方式:在派生类的构造函数中,为基类的构造函数提供参数。
derived::derived(arg_derived-list):base(arg_base-lisr)
隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数。
派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的析构函数。

封闭派生类对象的构造函数执行顺序

在创建派生类的对象时:
(1)先执行基类的构造函数,用以初始化派生类对象中从基类继承的成员;
(2)再执行成员对象类的构造函数,用以初始化派生类对象中的成员对象。
(3)最后执行派生类自己的构造函数。
在派生类对象消亡时:
(1)先执行派生类自己的析构函数
(2)再依次执行各成员对象类的析构函数
(3)最后执行基类的析构函数

public继承的赋值兼容规则

class base{};
class derived:public base{};
base b;
derived d;
1) 派生类对象可以赋值给基类对象 b=d;
2) 派生类对象可以初始化基类引用 base &br = d;
3) 派生类对象的地址可以赋值给基类指针 base *pb = &d;
如果派生方式是private或protected,则上述三条不可行。

protected继承和private继承
1
2
3
4
5
6
class base{
};
class derived:protected base{
};
base b;
derived d;

protected继承时,基类的public成员和protected成员为派生类的protected成员。
private继承时,基类的public成员成为派生类的private成员,基类的protected成员成为派生类的不可访问成员。
protected和private继承不是“是”的关系。

基类和派生类的指针强制转换(P238)

公有派生的情况下,派生类对象的指针可以直接赋值给基类指针 Base ptrBase = &objDerived;ptrBase指向的是一个Derived类的对象; ptrBase可以看做一个Base类的对象,访问它的public成员直接通过ptrBase即可,但不能通过ptrBase访问objDerived对象中属于Derived类而不属于Base类的成员。

即便基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类没有,而派生类中有的成员。

通过强制指针类型转换,可以把ptrBase转换成Derived类的指针

直接基类和间接基类

类A派生类B,类B派生类C,类C派生类D,….
-类A是类B的直接基类
-类B是类C的直接基类,类A是类C的间接基类,以此类推

在声明派生类时,只需要列出它的直接基类
-派生类沿着类的层次自动向上继承它的间接基类
-派生类的成员包括
派生类自己定义的成员
直接基类中的所有成员
所有间接基类的全部成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
using namespace std;

class Base{
public:
int n;
Base(int i):n(i){
cout << "Base" <<n << "constructed" << endl;
}
~Base() {
cout << "Base" << n << "destructed" << endl''
}
}

class Derived:public Base{
public:
Derived(int i):Base (i){
cout << "Derived constructed" << endl;
}
~Derived() {
cout << "Derived destructed" << endl;
}
};

class MoreDerived:public Derived {
public:
MoreDerived():Derived(4) {
cout << "More Derived constructed" << endl;
}
~MoreDerived() {
cout << "More Derived destructed" << endl;
}
};

int main()
{
MoreDerived Obj;
return 0;
}
0%