1.何为多态??多态的作用?
多态的概念:
一个接口,多种方法
多态的作用:
封装可以是得代码模块化;继承可以扩展已经存在的代码,都是为了代代码重用;
多态的目的:接口重用
2.静态联编和动态联编分别表示什么?
在编译的时候能够确定对象所调用的成员函数的地址则为静态联编;
动态联编:指的是在程序运行的时候动态地进行,根据当时的情况来确定调用哪个同名函数(父类指针指向哪个子类,就调用哪个子类的同名函数),实际上是在运行时虚函数的实现
3.类中有虚函数的时候,类有什么变化?
当类中存在虚函数的时候,这个类大小就增加4个字节,这4个字节是虚表指针,存放的是虚函数表的地址;
虚函数表其实是一个指针数组,它里面存放的其实是虚函数的地址
虚函数的几个知识点:
先看一个例子:
#include "stdafx.h"
#include <ostream>
#include <iostream>
using namespace std;
class CClassA {
public:
virtual void fun_a() {
cout << "fun:cclassA,这是类A里面的函数" << endl;
}
};
class CClassB:public CClassA {
public:
virtual void fun_a() {
cout << "fun:cclassB,这是类B里面的函数" << endl;
}
};
int main()
{
CClassA objA, *pobjA;
CClassB objB;
pobjA = &objA;
pobjA->fun_a();
pobjA = &objB;//父类指针指向对象B,下面这个函数就是B里面的对象!!!
pobjA->fun_a();
return 0;
}
几个重点:
一个空类:一个字节
一个空类里面只有一个整型,此类大小:4个字节
- 定义基类的公有派生类
- 基类的公有派生类中重载该虚函数
- 重载该虚函数不是一般的重载,它要求函数名,返回类型,参数个数,参数类型和顺序完全相同
- 由于对虚函数进行了重载,派生类中的虚函数前的virtual关键字可以省略.
- 父类虚表和子类虚表是2个独立的表(由于两个类之间有继承关系),故子类的虚表里面也有父类的虚函数指针,但是如果子类中有和父类中一样的函数名,那么子类中的虚函数就覆盖了子类虚表中所继承的父类虚函数地址;
- 如果父类中有虚函数,子类中无虚函数,则子类也会生成一个虚表,(因为子类要继承父类的虚函数,但是虚函数又不能随便放,只能生成一个虚表出来!)(以上为个人理解,没有太大偏差,想要理解的更深一点,需要查看相关文献!!!)
4.纯虚函数是一种特殊的虚函数,是一种没有具体实现的虚函数
例子:
class cclassA
{
virtual <函数类型><函数名>(<参数表>)=0;
}
含有纯虚函数的类是抽象类,抽象类是不能定义对象的(不能实例化)
含有纯虚函数的类---->抽象类------->不能定义对象!!!!!析构函数为什么要推荐设计为虚函数??
首先,先了解一下构造函数的调用顺序:
基类构造函数-->数据成员的构造函数->派生类构造函数
执行派生类的析构函数,也需要调用基类以及子对象的析构函数,析构顺序如下:
派生类析构函数-->数据成员类析构函数----->基类的析构函数
正常情况下,一个子类被释放的时候,会主动调用其父类析构函数;使用父类指针指向子类对象的时候,只会析构掉父类对象,如果此时子类里有堆空间内存,则会造成内存泄露!(内存泄露是指使用malloc或者new申请内存空间之后,没有freeh或者delete掉,此时申请的那块内存仍然处于占用状态,称为内存泄露!)
故必须将析构函数定义为虚函数,这样释放父类指针的时候便会调用子类的析构函数,也会正常释放掉子类的堆空间!
看下面一个例子:
#include "stdafx.h"
class Base
{
public:
Base() {
printf("base 父类构造\n");
};
virtual ~Base() { //1th在此行加不加virtual有一定区别,而对于一般成员函数,基类中有虚函数,则子类中对应的成员函数不一定声明为虚函数,因为子类继承了父类
printf("Base 父类析构\n");
};
void fun() { //2th
printf("父类base-fun\n");
};
};
class son :public Base
{
public:
son() {
printf("son 子类构造\n");
};
~son() { //**
printf("son 子类析构\n");
}
void fun() { //3th
printf("子类son-fun\n");
};
};
int main()
{
Base *p; //父类指针
son *pobj = new son; //走到这一步,会先调用父类构造,然后再调用子类构造,new 出子类对象指针,从堆空间中分配出来的,一般创建对象是在栈空间里面
p = pobj; //父类指针指向子类对象
p->fun(); //注意,这个例子里面函数不是虚函数,所以调用的函数皆为父类里面的函数!!!!!
delete pobj; //通过父类指针释放子类对象
return 0;
}
**处不管有没有virtual这个关键词,结果都如下:
(验证了上面所说的:如果父类中有虚函数,子类中无虚函数,则子类也会生成一个虚表)
- 虚基类的目的是为了解决二义性问题,使用公共基类在其派生类对象中只产生一个基类子对象!!!!!