cpp面向对象

继承

  • private(私有的):
    在private后声明的成员称为私有成员,私有成员只能通过本类的成员函数来访问。
    
  • public(公有的):
    在public后声明的成员称为公有成员,公有成员用于描述一个类与外部世界的接口,类的外部(程序的其它部分的代码)可以访问公有成员。
    
  • protected(受保护的):
    受保护成员具有private与public的双重角色:对派生类的成员函数而言,它为public,而对类的外部而言,它为private。即:protected成员只能由本类及其后代类的成员函数访问。
    

  • 派生类的对象不仅存放了在派生类中定义的非静态数据成员,而且也存放了从基类中继承下来的非静态数据成员

  • 可以认为派生类对象中包含了基类子对象。

派生类的构造函数和析构函数

  • 基类的构造函数不被继承,派生类中需要声明自己的构造函数。
  • 派生类的构造函数中只需要对本类中新增成员进行初始化即可。对继承来的基类成员的初始化是通过自动调用基类构造函数完成的。
  • 派生类的构造函数需要给基类的构造函数传递参数。

构造函数调用顺序

  • 首先调用其基类的构造函数(调用顺序按照基类被继承时的声明顺序(从左向右))。
  • 然后调用本类对象成员的构造函数(调用顺序按照对象成员在类中的声明顺序)。
  • 最后调用本类的构造函数。

撤销派生类对象时析构函数的调用次序与构造函数的调用次序相反

  • 首先调用本类的析构函数

  • 然后调用本类对象成员的析构函数

  • 最后调用其基类的析构函数

向基类构造函数传递实参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// IMPLEMENTATION FILE (exttime.cpp)

ExtTime::ExtTime( int initHrs,
int initMins,
int initSecs,
ZoneType initZone )
: Time(initHrs, initMins, initSecs)
{
zone = initZone;
}
//base class default constructor is called prior to the derived //class default constructor.
//派生类缺省构造函数时先调用基类的缺省构造函数

ExtTime::ExtTime()
{
zone = EST;
}

当继承的成员不能满足要求的派生类时,则应调整。

恢复的方法:

  • Resuming the access control 恢复访问控制

  • Redefinition of the inherited members. 重新定义继承成员

  • Rename of the inherited members .重命名继承成员

  • Hiding the inherited members. 隐藏继承成员

恢复访问控制

通过在派生类中声明,可以实现恢复访问控制的效果。如上图,这样就可以访问原本无法访问的set_i函数。

继承成员重定义

  • 派生类中修改继承成员函数的语义(即,修改函数体,而保持函数原型不变)。

  • 派生类中的名字支配(屏蔽)基类中的名字。

如上,通过重新定义派生类中继承的基类的成员函数,原型不变,结果变化。

逻辑如下:

重载成员函数

函数名相同,但函数首部不同(即参数列表不同;当然,函数实现一般也不同)。

利用重载,实现新的功能。

注意:重载不能只有返回值不同

类型兼容性

  • 参数传递与对象初始化的类型兼容性 与赋值运算的类型兼容性相同

继承类型

不支持:

多重继承:派生类有多于一个基类

C is both A and B

1
2
3
4
5
6
class 派生类名:继承访问控制1  基类名1,
继承访问控制2 基类名2,...
{
成员声明;
}

Notice:Each inheritance access control applies to its tailing base class ONLY.

名字冲突

多重继承中,当派生类的基类中有相同名字的成员时,当客户端代码访问此通过派生类的对象的名称,编译器不能决定使用哪个版本的成员,发生名字冲突。

解决方案:

  • 使用域运算符来明确地告诉使用哪个版本。
  • 在派生类中重新定义的冲突的成员。

example:

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
// Inheritance.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
using namespace std;
class BASE1 {
public: void show() { cout << i << "\n"; }
protected: int i;
};
class BASE2 {
public: void show() { cout << j << "\n"; }
protected: int j;
};

class DERIVED : public BASE1, public BASE2 {
public:
void set(int x, int y) { i = x; j = y; }
void show()
{
cout << i << "\n"; cout << j << "\n";
}
};

int main()
{
DERIVED obj; // 声明一个派生类的对象 
obj.set(5, 7); // set()是DERIVED类自身定义的
obj.show(); // 无二义性问题,调用的是DERIVED中新定义的版本

obj.BASE1::show(); // 仍然可调用从BASE1继承下来show()
obj.BASE2::show(); // 仍然可调用从BASE2继承下来show()

return 0;
} //程序EX4_12.cpp

在这里,对象obj可以通过obj.BASE1::show()调用BASE1的show,也可以调用BASE2的show,如果自己定义了show,可以直接调用。

多继承的构造和析构

调用基类构造函数的顺序:按继承声明中从左到右

1
2
3
4
5
6
class 派生类名:继承访问控制1  基类名1,
继承访问控制2 基类名2,...
{
成员声明;
}

先调用类1,再调用类2,析构顺序相反

多继承,可能会出现基类被继承多次

处理这种情况有两种方式:

  • Duplicate inheritance(复制继承):There are several copies of the base class members in a derived class object.派生类对象有基类成员的多个副本。
  • Shared inheritance(共享继承):There is only one copy of the base class members in a derived class object. 派生类对象只有基类成员的一个副本。

复制继承是默认的情况,但是可能会出现二义性:

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 BASE {public:	int i;}; 
class BASE1: public BASE {
public: int j;
};
class BASE2: public BASE {
public: int k;
}; 
class DERIVED: public BASE1, public BASE2 {
public: int sum;
}; 
int main()
{
DERIVED obj; // 声明一个派生类对象 
obj.i = 3; //错误,编译程序无法确定使用i的哪一份副本
obj.j = 5; //正确,使用从BASE1继承下来的j
obj.k = 7; //正确,使用从BASE2继承下来的k
} //程序EX4_15.cpp
Using :: explicitly明确
int main()
{
DERIVED obj;
obj.BASE1::i = 3; //正确
obj.BASE2::i = 4; //正确
……
}

这个时候就需要用域作用符来限定。

虚基类

  • 保留字 virtual 添加到继承访问控制的基类之前。然后该基类是虚基类。
  • 虚基类用于共享继承
  • 普通基类与虚基类之间的唯一区别只有在派生类重复继承了某一基类时才表现出来。

多态

多态性:对于相同的消息,会有不同的反应,产生不同的动作。面向对象的程序利用多态性使用同一接口链接各种对象的不同行为。**

c = a + b; (不同数据类型)

编译时多态性:

函数重载

运算符重载

运行时多态

运行时关联,动态绑定(通过继承+虚函数来实现)

绑定

绑定:函数调用和函数定义相关的过程。

静态绑定(早绑定)

  • 程序编译阶段完成
  • 应用于非虚函数

动态绑定(晚绑定)

  • 程序运行阶段完成
  • 应用于虚函数

虚函数

虚函数是加了’virtual’保留字的类成员函数

“一旦为虚,永远为虚”:基类的虚函数在派生类中始终为虚函数

主要作用:与继承相结合以实现运行时多态性。在公有继承层次中的一个或多个派生类中对虚函数进行重定义,然后通过指向基类的指针(或引用)调用虚函数来实现实现运行时多态性。