Coursera Cpp程序设计 week 4

这一周的内容主要是介绍继承和派生,下面回顾下。

课程地址:

coursera:C++程序设计

https://www.coursera.org/learn/cpp-chengxu-sheji

中国大学MOOC:程序设计与算法(三)C++面向对象程序设计

程序设计与算法(三)C++面向对象程序设计

继承和派生

基本概念

继承:在定义一个新的类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
16
17
18
19
20
21
22
23
24
25
26
class CStudent {
private:
string sName;
int nAge;
public:
bool IsThreeGood() { };
void SetName( const string & name )
{ sName = name; }
//......
};

class CUndergraduateStudent: public CStudent {
private:
int nDepartment;
public:
bool IsThreeGood() { ...... }; //覆盖
bool CanBaoYan() { .... };
}; // 派生类的写法是:类名: public 基类名

class CGraduatedStudent:public CStudent {
private:
int nDepartment;
char szMentorName[20];
public:
int CountSalary() { ... };
};

派生类对象的内存空间

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

继承关系和复合关系

来比较下继承关系和复合关系的区别。

继承:“是”关系。

  • 基类 A, B是基类A的派生类。
  • – 逻辑上要求:“一个B对象也是一个A对象”。

复合:“有”关系。

  • 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系。
  • 一般逻辑上要求:“D对象是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
class base {
int j;
public:
int i;
void func();
};

class derived :public base{
public:
int i;
void access();
void func();
};

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

derived obj;
obj.i = 1;
obj.base::i = 1;

一般来说,基类和派生类不定义同名成员变量。

类的保护成员

来介绍另一种存取权限说明符:protected,来分别看下几种存取权限说明符的访问范围:

基类的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
24
25
26
27
class Father {
private: int nPrivate; //私有成员
public: int nPublic; //公有成员
protected: int nProtected; // 保护成员
};
class Son :public Father{
void AccessFather () {
nPublic = 1; // ok;
nPrivate = 1; // wrong
nProtected = 1; // OK,访问从基类继承的protected成员
Son f;
f.nProtected = 1; //wrong , f不是当前对象
}
};

int main()
{
Father f;
Son s;
f.nPublic = 1; // Ok
s.nPublic = 1; // Ok
f.nProtected = 1; // error
f.nPrivate = 1; // error
s.nProtected = 1; //error
s.nPrivate = 1; // error
return 0;
}

派生类的构造函数

1.在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数之前,总是先执行基类的构造函数。

2.调用基类构造函数的两种方式

  • 显式方式:在派生类的构造函数中,为基类的构造函数提供参数。
    • derived::derived(arg_derived-list):base(arg_base-list)
  • 隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数。

3.派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的析构函数。

来看一个例子:

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
class Bug {
private :
int nLegs; int nColor;
public:
int nType;
Bug ( int legs, int color);
void PrintBug (){ };
};

class FlyBug: public Bug // FlyBug是Bug的派生类
{
int nWings;
public:
FlyBug( int legs,int color, int wings);
};

Bug::Bug( int legs, int color)
{
nLegs = legs;
nColor = color;
}

//错误的FlyBug构造函数
FlyBug::FlyBug ( int legs,int color, int wings)
{
nLegs = legs; // 不能访问
nColor = color; // 不能访问
nType = 1; // ok
nWings = wings;
}
//正确的FlyBug构造函数:
FlyBug::FlyBug ( int legs, int color, int wings):Bug( legs, color)
{
nWings = wings;
}
封闭派生类对象消亡时析构函数的执行顺序

在创建派生类的对象时:

1) 先执行基类的构造函数,用以初始化派生类对象中从基类继承的成员。

2) 再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。

3) 最后执行派生类自己的构造函数。

在派生类对象消亡时:

1) 先执行派生类自己的析构函数

2) 再依次执行各成员对象类的析构函数

3) 最后执行基类的析构函数

析构函数的调用顺序与构造函数的调用顺序相反。

public继承的赋值兼容规则

1
2
3
4
class base { };
class derived : public base { };
base b;
derived d;

1) 派生类的对象可以赋值给基类对象 b = d;

2) 派生类对象可以初始化基类引用 base & br = d;

3) 派生类对象的地址可以赋值给基类指针 base * pb = & d;

如果派生方式是 private或protected,则上述三条不可行。

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

直接基类与间接基类

在声明派生类时,只需要列出它的直接基类,派生类沿着类的层次自动向上继承它的间接基类。

派生类的成员包括 :

1)派生类自己定义的成员

2)直接基类中的所有成员

3)所有间接基类的全部成员