C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数,多态分为静态多态与动态多态。
多态使用条件:父类指针或引用指向子类
1.多态基本概念
1.1.静态多态
静态多态:函数重载和运算符重载等复用函数名为静态多态
1.2.动态多态
动态多态:派生类和虚函数实现运行时多态,需要在子类中对父类的虚函数进行重写
虚函数: virtual +函数
//父类引用指向子类
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
void dospeak(Animal& animal)//Animal & animal = cat;父类指针可以指向子类
{
animal.speak();
}
void test01()
{
Cat cat;
dospeak(cat);
Dog dog;
dospeak(dog);
}
int main()
{
test01();
return 0;
}
在不同类的对象调用dospeak函数时实现了不同的功能。
//父类指针指向子类
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
void test01()
{
Animal* ptr = new Cat;
ptr->speak();
delete ptr;
Dog dog;
ptr = new Dog;
ptr->speak();
delete ptr;
}
int main()
{
test01();
return 0;
}
2.纯虚函数与抽象类
纯虚函数:没有函数体,同时在定义的时候,其函数名后面要加上“= 0”。
语法:virtual 返回值类型 函数名(参数列表) = 0;
virtual void speak() = 0;
抽象类:类中有了纯虚函数,则为抽象类。
抽象类无法实例化对象,子类必须重写父类中的纯虚函数不然也视为抽象类。
Animal a;//不允许使用抽象类实例化对象
3.虚析构与纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构函数。
解决:将父类中的析构函数改为虚析构或者纯虚析构
虚析构语法:virtual ~类名() {};
纯虚析构语法:virtual ~类名() = 0;
添加纯虚析构后,父类也变为抽象类,不可实例化对象。
但是虚析构与纯虚析构函数不仅要声明,也需要实现。
虚析构与纯虚析构不共存。
// 父类指针指向子类
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal()
{
cout << "Animal构造函数调用" << endl;
}
virtual void speak() = 0;
//虚析构解决 父类指针释放子类对象不干净的问题
~Animal()
{
cout << "Animal析构函数调用" << endl;
}
//纯虚析构
//virtual ~Animal() = 0;
};
//Animal::~Animal()
//{
// cout << "Animal纯虚析构调用" << endl;
//}
class Cat :public Animal
{
public:
Cat(string name)
{
cout << "Cat构造函数调用" << endl;
m_name = new string (name);
}
void speak()
{
cout <<*m_name<< "小猫在说话" << endl;
}
string* m_name;
~Cat()
{
cout << "Cat析构函数调用" << endl;
if (m_name != NULL)
{
delete m_name;
m_name = NULL;
}
}
};
void test01()
{
//Animal a;不允许使用抽象类实例化对象
Animal* ptr = new Cat("Ton");
ptr->speak();
delete ptr;
}
int main()
{
test01();
return 0;
}
输出为:
Animal构造函数调用
Cat构造函数调用
Ton小猫在说话
Animal纯虚析构调用
此时程序并未调用子类的析构函数,存在父类指针释放子类对象不干净的问题,采用虚析构或者纯虚析构的方式对子类对象进行释放。
3.1虚析构
将普通的析构函数改成虚析构函数
//虚析构解决 父类指针释放子类对象不干净的问题
virtual ~Animal()
{
cout << "Animal析构函数调用" << endl;
}
此时的输出为:
Animal构造函数调用
Cat构造函数调用
Ton小猫在说话
Cat析构函数调用
Animal析构函数调用
可见子类对象的析构函数被调用
3.2纯虚析构
用纯虚析构时要函数体不可为空,需要在类外对函数体内容进行丰富,补充以下代码:
//纯虚析构
virtual ~Animal() = 0;
类外:
Animal::~Animal()
{
cout << "Animal纯虚析构调用" << endl;
}
此时的输出为:
Animal构造函数调用
Cat构造函数调用
Ton小猫在说话
Cat析构函数调用
Animal纯虚析构调用
4.综合案例
描述:用多态的方式实现对电脑的组装,电脑类中是各种部件的指针用于对部件的访问。
// 父类指针指向子类
#include <iostream>
#include <string>
using namespace std;
class GPU
{
public:
virtual void GPUwork()
{
};//虚函数
};
class IntelGPU:public GPU
{
void GPUwork()//重写父类中的虚函数
{
cout << "IntelGPU已配置" << endl;
}
};
class LenovoGPU:public GPU
{
void GPUwork()//重写父类中的虚函数
{
cout << "LenovoGPU已配置" << endl;
}
};
class Memory
{
public:
virtual void Memorywork()= 0;//纯虚函数
};
class IntelMemory :public Memory
{
void Memorywork()//重写父类中的虚函数
{
cout << "IntelMemory已配置" << endl;
}
};
class LenovoMemory :public Memory
{
void Memorywork()//重写父类中的虚函数
{
cout << "LenovoMemory已配置" << endl;
}
};
class Computer
{
public:
Computer(Memory* memory,GPU* gpu)
{
m_memory = memory;
m_gpu = gpu;
}
Memory* m_memory;
GPU* m_gpu;
void assemble()
{
this->m_gpu->GPUwork();
this->m_memory->Memorywork();
}
~Computer()//析构函数释放部件指针所指的空间
{
if (m_gpu != NULL)
{
delete m_gpu;
m_gpu = NULL;
}
if (m_memory != NULL)
{
delete m_memory;
m_memory = NULL;
}
}
};
void test01()
{
Memory* memory = new IntelMemory;//父类指针指向子类
GPU* gpu = new IntelGPU;//父类指针指向子类
cout << "第一台电脑已经装配" << endl;
Computer* ptr = new Computer(memory, gpu);//初始化一个Computer类指针变量
ptr->assemble();
delete ptr;
Computer* ptr1 = new Computer((new IntelMemory), (new LenovoGPU));
cout << "第二台电脑已经装配" << endl;
ptr1->assemble();
delete ptr1;
};
int main()
{
test01();
return 0;
}
总结
多态的写法在查找错误时可以只查看出问题的代码