C++ 多态
静态联编动态联编
- 静态多态在编译阶段绑定地址,地址早绑定,静态联编。
- 动态多次在运行阶段绑定地址,地址晚绑定,动态联编。
静态多态
函数重载(函数名相同,函数列表不同),运算符重载
动态多态
- 先有继承关系
- 父类中有虚函数,子类重写父类中的虚函数
- 父类的指针或引用 指向子类的对象
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
virtual void eat(int a )
{
cout << "动物在吃饭" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
void eat(int a)
{
cout << "小猫在吃饭" << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
void doSpeak(Animal & animal)
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat);
Dog dog;
doSpeak(dog);
}
多态原理
class Animal
{
public:
void speak()
{
cout << "动物在说话" << endl;
}
};
cout << "sizeof Animal = " << sizeof (Animal) << endl;
- 当父类写了虚函数后,类内部结构发生改变,多了一个vfptr
- vfptr 虚函数表指针 ---- > vftable 虚函数表
- 虚函数表内部记录着 虚函数的入口地址
- 当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找函数入口地址
虚函数 关键字 virtual 利用指针的偏移调用 函数

Cat 未发生重写时  Cat 重写后 
使用指针偏移方式调用虚函数
void test02()
{
Animal * animal = new Cat;
animal->speak();
((void(*)()) (*(int *)(*(int *)animal))) ();
typedef void( __stdcall *FUNPOINT)(int);
(FUNPOINT (*((int*)*(int*)animal + 1)))(10);
}
- 主调函数和被调函数必须要有一致约定,才能正确的调用函数,这个约定我们称为调用惯例
- 调用惯例 包含内容: 出栈方、参数传递顺序、函数名称修饰
C/C++ 下默认调用惯例: cdecl 从右到左 ,主调函数管理出栈
((void(*)()) (*(int *)*(int *)animal)) ();
typedef void( __stdcall *FUNPOINT)(int);
(FUNPOINT (*((int*)*(int*)animal + 1)))(10);
纯虚函数和抽象类
- 语法:
virtual int getResult() = 0; - 如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为
抽象类 - 抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于
抽象类
class AbstractCalculator
{
public:
virtual int getResult() = 0;
virtual int getVirtualResult()
{
return 0;
}
int m_A;
int m_B;
};
虚析构和纯虚析构
虚析构语法:
virtual ~Animal(){} - 如果子类中有指向堆区的属性,那么要利用虚析构技术 ,在
delete 的时候 调用子类的析构函数,添加虚析构后才会调用子类的析构函数
纯虚析构语法:
virtual ~Animal() = 0; Animal::~Animal(){ .. } - 纯虚析构,需要有声明,也需要有实现(类内声明,类外实现)
- 如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
class Animal
{
public:
Animal()
{
cout << "Animal的构造函数调用" << endl;
}
virtual void speak()
{
cout << "动物在说话" << endl;
}
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout << "Animal的纯虚析构函数调用" << endl;
}
class Cat :public Animal
{
public:
Cat(const char* name)
{
cout << "Cat的构造函数调用" << endl;
this->m_Name = new char[strlen(name) + 1];
strcpy(this->m_Name, name);
}
virtual void speak()
{
cout << this->m_Name << " 小猫在说话" << endl;
}
~Cat()
{
if (this->m_Name)
{
cout << "Cat的析构函数调用" << endl;
delete[] this->m_Name;
this->m_Name = NULL;
}
}
char* m_Name;
};

向上类型转换和向下类型转换
- 父转子 :向下类型转换 ,不安全,会出现地址越界
Animal * animal = new Animal;
Cat * cat = (Cat *) animal;
- 子转父 向上类型转换 ,安全,仅仅是取址范围缩小
Cat * cat = new Cat;
Animal * animal = (Animal *) cat;
- 如果发生多态,那么转换永远都是安全的,父类子针或引用指向子类对象
Animal * animal = new Cat;
Cat * cat = (Cat * ) animal;
Animal * animal = new Cat; 执行时已经开辟出了Cat 所需要的内存,只是当用animal 指针指向时取址范围为Animal 大小,当强转回Cat 时,使用原始地址范围。 
重载、重写、重定义
重载
同一作用域的同名函数
- 同一个作用域下
- 参数个数,参数顺序,参数类型不同
- 和函数返回值,没有关系
const 也可以作为重载条件
do(const Teacher& t){}
do(Teacher& t){}
重写(覆盖)
子类重写父类中的虚函数,函数返回值、函数名、形参列表完全一致
- 有继承
- 子类重写父类的
virtual 函数 - 函数返回值,函数名字,函数参数必须和基类中的虚函数一致
重定义(隐藏)
子类重新定义父类中的同名成员函数,隐藏掉父类中同名成员函数,如果想调用加作用域
- 有继承
- 子类重新定义父类的同名成员(非
virtual 函数)
多态案例2 - 电脑组装案例
class CPU
{
public:
virtual void calculate() = 0;
};
class VideoCard
{
public:
virtual void display() = 0;
};
class Memory
{
public:
virtual void storage() = 0;
};
class computer
{
public:
computer(CPU * cpu, VideoCard * vc, Memory * mem)
{
cout << "电脑构造调用" << endl;
this->m_Cpu = cpu;
this->m_Vc = vc;
this->m_Mem = mem;
}
void doWork()
{
this->m_Cpu->calculate();
this->m_Vc->display();
this->m_Mem->storage();
}
~computer()
{
cout << "电脑析构调用" << endl;
if (this->m_Cpu)
{
delete this->m_Cpu;
this->m_Cpu = NULL;
}
if (this->m_Vc)
{
delete this->m_Vc;
this->m_Vc = NULL;
}
if (this->m_Mem)
{
delete this->m_Mem;
this->m_Mem = NULL;
}
}
CPU * m_Cpu;
VideoCard * m_Vc;
Memory * m_Mem;
};
class intelCPU :public CPU
{
public:
void calculate()
{
cout << "intelCPU开始计算了" << endl;
}
};
class intelVideoCard :public VideoCard
{
public:
void display()
{
cout << "intel 显卡开始显示了" << endl;
}
};
class intelMemory :public Memory
{
public:
void storage()
{
cout << "intel 内存条开始存储了" << endl;
}
};
class LenovoCPU :public CPU
{
public:
void calculate()
{
cout << "Lenovo CPU开始计算了" << endl;
}
};
class LenovoVideoCard :public VideoCard
{
public:
void display()
{
cout << "Lenovo 显卡开始显示了" << endl;
}
};
class LenovoMemory :public Memory
{
public:
void storage()
{
cout << "Lenovo 内存条开始存储了" << endl;
}
};
void test01()
{
cout << "第一台电脑组成:" << endl;
CPU * intelCpu = new intelCPU;
VideoCard * lenovoVC = new LenovoVideoCard;
Memory * lenovoMem = new LenovoMemory;
computer c1(intelCpu, lenovoVC, lenovoMem);
c1.doWork();
cout << "第二台电脑组成:" << endl;
CPU * intelCpu2 = new LenovoCPU;
VideoCard * lenovoVC2 = new intelVideoCard;
Memory * lenovoMem2 = new intelMemory;
computer c2(intelCpu2, lenovoVC2, lenovoMem2);
c2.doWork();
}
|