文章目录
- C++面向对象之多态性
- 1.静态多态
- 2.动态多态
- 3.多态的好处
- 3.1使用方法
- 4.纯虚函数
- 5.虚析构与纯虚析构
- 5.1问题
- 5.2解决
- 6.其他知识点
- 7.代码
- 8.测试结果
- 8.1父类中无虚函数,父类的指针指向子类对象,将调用父类中的函数,无法调用子类中的重写函数,即无法实现多态
- 8.2父类中的函数由虚函数实现
- 8.3父类指针对象,无法释放子类中开辟的堆空间数据
- 8.4将父类的析构函数定义为虚析构函数
- 8.5将父类中的析构函数定义为纯虚析构函数
C++面向对象之多态性
1.静态多态
通过函数重载和运算符重载实现,在编译时确定函数早绑定
2.动态多态
派生类和虚函数实现运行时多态,运行阶段实现函数晚绑定。
当子类重写了父类的虚函数时,子类中的虚函数表将替换父类中的虚函数,
所以当父类的引用或者指针指向子类对象时,将发生多态
3.多态的好处
结构清晰
可读性很强
利于后期的拓展和维护
3.1使用方法
父类的引用或者指针指向子类的对象
4.纯虚函数
定义格式:virtual 返回值类型 函数名称(参数列表) = 0;
类中有了纯虚函数,称为抽象类,抽象类无法实例化对象
子类必须重写父类中的所有纯虚函数,才能实例化对象,否则也是抽象类
5.虚析构与纯虚析构
5.1问题
多态使用时,如果子类中有属性开辟到了堆区,则父类指针释放时无法调用子类的析构函数
5.2解决
将父类中的析构函数改为虚析构函数或者纯虚析构函数
纯虚析构函数需要先声明,后实现,但是纯虚函数可以只声明
如果子类没有堆区数据,可以不将父类的析构函数写成虚析构或者纯虚析构
拥有纯虚析构函数的类也属于抽象类
6.其他知识点
空类(即里面什么代码都没有)的大小通过sizeof测定的结果是1
vfptr(虚函数表指针) ——> vftable(虚函数表,用于记录虚函数的地址)
7.代码
#include<iostream>
using namespace std;
class Animal {
public:
Animal() {
cout << "基类中的构造函数被调用!" << endl;
}
// 虚函数,虚函数将实现晚绑定
virtual void speack() {
cout << "动物在说话!" << endl;
}
virtual ~Animal() = 0; // 纯虚析构函数,纯虚析构声明后,必须定义
};
// Animal中的纯虚析构实现部分,可以在这里释放父类开辟的堆空间
Animal::~Animal() {}
class Cat :public Animal {
public:
Cat() {
cout << "cat中的构造函数被调用" << endl;
}
void speack() {
cout << "小猫在说话!" << endl;
}
~Cat() {
cout << "cat中的析构函数被调用" << endl;
}
};
class Dog : public Animal {
public:
void speack() {
cout << "小狗在说话" << endl;
}
~Dog() {
cout << "dog中的析构函数被调用" << endl;
}
};
// 地址早绑定,在编译阶段函数地址已经早绑定Animal
void doSpeack(Animal& animal) {
animal.speack();
}
// 派生类重写基类中的虚函数,当基类的引用或者指针指向派生类时,实现多态
void test01() {
Cat cat;
Dog dog;
doSpeack(cat);
doSpeack(dog);
}
// 基类和派生类的构造函数和析构函数的调用
//
void test02() {
Animal* p_cat = new Cat(); // 开辟内存到堆区,需要手动释放
if (p_cat != NULL) {
delete p_cat; // 释放指针
p_cat = NULL;
}
}
// --------------------------------------------------------------------------
class CPU {
public:
virtual void caculate() = 0;
};
class VideoCard {
public:
virtual void display() = 0;
};
class Memory {
public:
virtual void storage() = 0;
};
class Computer {
public:
Computer(CPU *cpu, VideoCard *videoCard,Memory * memory) {
this -> m_cpu = cpu;
this -> m_vc = videoCard;
this -> m_mem = memory;
}
// 电脑的工作
void work() {
m_cpu->caculate();
m_vc->display();
m_mem->storage();
}
~Computer() {
if (m_cpu != NULL) {
delete m_cpu;
}
if (m_vc != NULL) {
delete m_vc;
}if (m_mem != NULL) {
delete m_mem;
}
}
private:
CPU* m_cpu;
VideoCard* m_vc;
Memory* m_mem;
};
// inter厂商
class InterCPU :public CPU {
public:
void caculate() {
cout << "inter的CPU开始计算了" << endl;
}
};
class InterVideoCard :public VideoCard {
public:
void display() {
cout << "inter的显卡开始计算了" << endl;
}
};
class InterMemory :public Memory {
public:
void storage() {
cout << "inter的内存条开始存储了" << endl;
}
};
// lenovo
class lenovoCPU :public CPU {
public:
virtual void caculate() {
cout << "lenovo的CPU开始计算了" << endl;
}
};
class lenovoVideoCard :public VideoCard {
public:
virtual void display() {
cout << "lenovo的显卡开始计算了" << endl;
}
};
class lenovoMemory :public Memory {
public:
virtual void storage() {
cout << "lenovo的内存条开始存储了" << endl;
}
};
void test03() {
// 创建电脑
CPU* interCPU = new InterCPU;
VideoCard* interCard = new InterVideoCard;
Memory* interMemory = new InterMemory;
Computer* computer1 = new Computer(interCPU, interCard, interMemory);
computer1->work();
delete computer1;
cout << "---------------------------" << endl;
Computer* computer2 = new Computer(new lenovoCPU, new lenovoVideoCard,new lenovoMemory);
computer2->work();
delete computer2;
cout << "---------------------------" << endl;
Computer* computer3 = new Computer(new InterCPU, new lenovoVideoCard, new lenovoMemory);
computer3->work();
delete computer3;
}
int main() {
//test01();
//test02();
test03();
system("pause");
return 0;
}
8.测试结果
8.1父类中无虚函数,父类的指针指向子类对象,将调用父类中的函数,无法调用子类中的重写函数,即无法实现多态
8.2父类中的函数由虚函数实现
父类的指针指向小猫子类,将调用小猫类中的重写函数,实现多态
8.3父类指针对象,无法释放子类中开辟的堆空间数据
cat类中的析构函数没有被调用,无法释放开辟的堆空间数据
8.4将父类的析构函数定义为虚析构函数
子类中的析构函数被调用
8.5将父类中的析构函数定义为纯虚析构函数
子类中的析构函数被调用,将父类中的析构函数声明为纯虚析构函数后,必须要实现声明的函数。