父类指针可以指向子类对象,是安全的,开发中经常用到(继承方式必须是public)
多态
◼ 默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态
◼ 多态是面向对象非常重要的一个特性
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
在运行时,可以识别出真正的对象类型,调用对应子类中的函数
◼ 多态的要素
子类重写父类的成员函数(override)
父类指针指向子类对象
利用父类指针调用重写的成员函数
#include <iostream>
using namespace std;
struct Animal {
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Dog : Animal {
// 重写(覆写、覆盖、override)
void speak() {
cout << "Dog::speak()" << endl;
}
void run() {
cout << "Dog::run()" << endl;
}
};
struct Cat : Animal {
void speak() {
cout << "Cat::speak()" << endl;
}
void run() {
cout << "Cat::run()" << endl;
}
};
struct Pig : Animal {
void speak() {
cout << "Pig::speak()" << endl;
}
void run() {
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p) {
p->speak();
p->run();
}
int main() {
//父类指针 指向 子类对象
/*Animal *p = new Pig();
p->speak();
p->run();*/
//Animal *p = new Dog();
//p->speak(); // call Animal::speak
//p->run(); // call Animal::run
liu(new Dog());
liu(new Cat());
liu(new Pig());
getchar();
return 0;
}
虚函数
◼ C++中的多态通过虚函数(virtual function)来实现
◼ 虚函数:被virtual修饰的成员函数
◼ 只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(也就是说子类中可以省略virtual关键字)
◼ 虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表
如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表
#include <iostream>
using namespace std;
class Animal {
public:
int m_age;
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
class Cat : public Animal {
public:
int m_life;
void speak() {
cout << "Cat::speak()" << endl;
}
void run() {
cout << "Cat::run()" << endl;
}
};
int main() {
// 动态性
Animal *cat0 = new Cat();
//子类对象在创建时会判断是否有虚函数,有则需要在最前面放虚表,虚表里面存放要调用的虚函数地址
cat0->m_age=20;
cat0->speak();
cat0->run();
delete cat0;
/*Animal *cat0 = new WhiteCat();
cat0->speak();
cat0->run();
WhiteCat *cat1 = new WhiteWhiteCat();
cat1->speak();
cat1->run();*/
/*Animal *anim = new Animal();
anim->speak();
anim->run();
Animal *cat = new Cat();
cat->speak();
cat->run();*/
getchar();
return 0;
}
虚表指针在对象地址的最前面,有虚函数则有虚表
虚析构函数
◼ 如果存在父类指针指向子类对象的情况,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类的析构函数,保证析构的完整性
纯虚函数
◼ 纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范
◼ 抽象类(Abstract Class)
含有纯虚函数的类,不可以实例化(不可以创建对象)
抽象类也可以包含非纯虚函数、成员变量
如果父类是抽象类,子类没有完全重写纯虚函数,那么这个子类依然是抽象类
// Java:抽象类、接口
// OC:协议
class Animal {
virtual void speak() = 0;
virtual void run() = 0;
};