1、第50节
C++中的类对象在内存布局上与结构体相同
访问权限关键字在运行时失效
2、第51节
子类是由父类成员叠加子类新成员得到的
- C++多态
相同的行为方式:同一条调用语句
不同的行为结果:同一条调用语句得到不同的执行结果
virtual void print()
{
cout << "I'm Parent." << endl;
}
void print() //由于继承没必要写virtual,也是虚函数
{
cout << "I'm Child." << endl;
}
void how_to_print(Parent* p)
{
p->print(); //展现多态的行为
}
- C++多态的实现原理
— 当类中声明虚函数时,编译器会在类中生成一个虚函数表
— 虚函数表是一个存储成员函数地址的数据结构
— 虚函数表是由编译器自动生成与维护的
— virtual 成员函数会被编译器放入虚函数表中
— 存在虚函数时,每个对象中都有一个指向虚函数表的指针,这个指针一般放在最开头的位置、
分析:一般一个父类,一个子类。父类里面定义虚函数。当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个 存储成员函数地址 的地方,也就是函数指针,要知道虚函数的类型都是一样的,所以里面存放一个函数指针,却可以指向两个虚函数。
1、我们把虚函数表伪装成结构体,父类里面的第一个成员就是虚函数表类型的指针。
2、定义虚函数表指针的成员变量,指向一个虚函数表的对象(也就是用一个虚函数表伪装的结构体对象对它进行初始化)
3、真正的虚函数时另一种类型,我们看到的虚函数其实只是一个外部包装的,其实里面的实现都是一直用指针指向到另一种类型的虚函数。
3、第52节
-
面向对象中的抽象类
— 可用于表示现实世界中的抽象概念
— 是一种只能定义类型,而不能产生对象的类
— 只能被继承并重写相关函数
— 直接特征是相关函数没有完整的实现 -
C++语言中没有抽象类的概念
-
C++通过纯虚函数实现抽象类
-
纯虚函数是指只定义原型的成员函数
-
一个C++类中纯在纯虚函数就成为了抽象类
纯虚函数的语法规则:
class Shape
{
public:
virtual double area() = 0;
};
“= 0” 用于告诉编译器当前是声明纯虚函数,因此不需要定义函数体
- 抽象类只能用作父类被继承
- 子类必须实现虚函数的具体功能
- 纯虚函数被实现后成为虚函数
- 如果子类没有实现纯虚函数,则子类成为抽象类
— 满足下面的条件的C++类则成为接口
1、类中没有定义任何的成员变量
2、所有的成员函数都是公有的
3、所有的成员函数都是纯虚函数
4、接口是一种特殊的抽象类
4、第53节
-
多重继承问题1
通过多重继承得到的对象可能拥有“不同的地址”
在这里pa 和 pb 都是通过父类指针指向子类对象,指向相同的对象但是地址确实不一样的。 -
多重继承问题2
多重继承可能产生冗余的成员
解决方案:虚继承
class People{ };
class Teacher:virtual public People
{
};
class Student:virtual public People
{
};
class Doctor:public People,public Student
{
};
- 虚继承能够解决数据冗余的问题
- 中间层父类不再关心顶层父类的初始化
- 最终子类必须直接调用顶层父类的构造函数
改成:
class Doctor:public Student,public Teacher
{
public:
Doctor(string name, int age) :Teacher(name, age), Student(name, age)
{
}
};
多重继承容易带来问题
— 可能出现“同一个对象的地址不同”的情况
— 虚继承可以解决数据冗余的问题
— 虚继承使得架构设计可能出现问题
- 多重继承问题3
多重继承可能产生多个虚函数表。
需要进行强制类型转换时应该用 dynamic_cast
- 工程中常用单继承加多接口、
5、第54节
关于虚函数
- 构造函数不可能成为虚函数
构造函数执行结束之后,虚函数表指针才会被正确的初始化 - 析构函数可以成为虚函数
建议在设计类的时候将析构函数声明为虚函数 - 构造函数中不可能发生多态行为
在构造函数执行时,虚函数表指针未被正确初始化 - 析构函数中不可能发生多态行为
在析构函数执行时,虚函数表指针已经被销毁
*** 关于继承中的强制类型转换
条件:
1、dynamic_cast 是与继承相关的类型转换关键字
2、dynamic_cast 要求相关的类中必须有虚函数
- 用于直接或间接继承关系的指针(引用)之间
— 指针:
转换成功:得到目标类型的指针
转换失败:得到一个空指针
— 引用:
转换成功:得到目标类型的引用
转换失败:得到一个异常操作信息
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
}
virtual ~Base()
{
}
};
class Derived : public Base
{
public:
Derived()
{
}
~Derived()
{
}
};
int main()
{
Base* p = new Derived;
Derived* pd = dynamic_cast<Derived*>(p);
cout << "pd = " << pd << endl;
return 0;
}