1. O conceito básico de polimorfismo
O polimorfismo é uma das três principais características do C ++ orientado a objetos
O polimorfismo é dividido em duas categorias
- Polimorfismo estático: a sobrecarga de função e a sobrecarga de operador pertencem ao polimorfismo estático, e os nomes de função são reutilizados
- Polimorfismo dinâmico: classes derivadas e funções virtuais para atingir o polimorfismo em tempo de execução
A diferença entre polimorfismo estático e polimorfismo dinâmico:
- A ligação inicial de endereços de função polimórfica estática-endereços de função são determinados durante a compilação
- Endereço de função polimórfica dinâmica ligação tardia - o endereço da função é determinado em tempo de execução
Vamos explicar o polimorfismo por meio de um caso
class Animal
{
public:
//Speak函数就是虚函数
//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。
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.speak();
}
//
//多态满足条件:
//1、有继承关系
//2、子类重写父类中的虚函数
//多态使用:
//父类指针或引用指向子类对象
void test01()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
int main() {
test01();
system("pause");
return 0;
}
Dois, funções virtuais puras e classes abstratas
No polimorfismo, geralmente a implementação de funções virtuais na classe pai não tem sentido, e o conteúdo principal é chamar a subclasse para reescrever
Portanto, a função virtual pode ser alterada para uma função virtual pura
Sintaxe de função virtual pura:virtual 返回值类型 函数名 (参数列表)= 0 ;
Quando há funções virtuais puras na classe, essa classe também é chamadaAula abstrata
Recursos abstratos :
- Não é possível instanciar o objeto
- A subclasse deve reescrever a função virtual pura na classe abstrata, caso contrário, ela também pertence à classe abstrata
class Base
{
public:
//纯虚函数
//类中只要有一个纯虚函数就称为抽象类
//抽象类无法实例化对象
//子类必须重写父类中的纯虚函数,否则也属于抽象类
virtual void func() = 0;
};
class Son :public Base
{
public:
virtual void func()
{
cout << "func调用" << endl;
};
};
void test01()
{
Base * base = NULL;
//base = new Base; // 错误,抽象类无法实例化对象
base = new Son;
base->func();//此时调用的是子类Son的func函数
delete base;//因为是在堆区(使用了new)记得销毁
}
int main() {
test01();
system("pause");
return 0;
}
Três, destruição fictícia e destruição pura fictícia
Ao usar polimorfismo, se houver propriedades na subclasse que são abertas para a área de heap, o ponteiro da classe pai não pode ser chamado para o código destruidor da subclasse quando for lançado
Solução: mude o destruidor na classe pai para destruição virtual ou destruição virtual pura
A semelhança entre a análise fictícia e a análise fictícia pura:
- Pode resolver o ponteiro da classe pai e liberar o objeto da subclasse
- Precisa ter implementação de função específica
A diferença entre destruição fictícia e destruição fictícia pura:
- Se for pura destruição fictícia, a classe pertence a uma classe abstrata e o objeto não pode ser instanciado
Gramática de análise fictícia:
virtual ~类名(){}
Sintaxe de análise fictícia pura:
virtual ~类名() = 0;
类名::~类名(){}
class Animal {
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
virtual void Speak() = 0;
//析构函数加上virtual关键字,变成虚析构函数
//virtual ~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);
}
virtual void Speak()
{
cout << *m_Name << "小猫在说话!" << endl;
}
//只需要在父类中写(纯)虚析构函数即可,子类中不需要
~Cat()
{
cout << "Cat析构函数调用!" << endl;
if (this->m_Name != NULL) {
delete m_Name;
m_Name = NULL;
}
}
public:
string *m_Name;
};
void test01()
{
Animal *animal = new Cat("Tom");
animal->Speak();
//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
//怎么解决?给基类增加一个虚析构函数
//虚析构函数就是用来解决通过父类指针释放子类对象
//如果把这句话注释掉,那么父类、子类的析构函数都不会被调用,所以,这就说明;
//在这种父类指针指向子类对象的多态结构中,子类对象其实是一直依赖着父类指针,
//自己其实并没有单独生成,所以也不会自己在函数执行完调用析构;
//而父类因为是在堆区,所以也不会自动释放
delete animal;
}
int main() {
test01();
system("pause");
return 0;
}