目录
多态是什么?
多态的使用
多态满足条件
多态使用条件
重写的概念
纯虚函数和抽象类
虚析构和纯虚析构
虚析构
虚析构函数
纯虚析构函数,不可只向纯虚函数一样只做声明,还需要析构函数实现
多态实现计算器
new ClassObj(constructAttribute) 和 new int(n)
C++中的多态性是面向对象编程的重要特性之一,它允许不同的子类对象对同一消息做出独特的响应。C++中的多态性可以通过虚函数和继承来实现
静态多态性和动态多态性:
虚函数和动态多态性:
virtual
关键字声明的成员函数。它允许在派生类中对其进行重写,从而实现运行时多态性。纯虚函数和抽象类:
virtual
关键字声明并赋予 0 初始值的虚函数。它使得基类成为抽象类,无法实例化对象,但可以作为其他类的基类。多态性的优势:
父类指针或引用指向子类对象
函数返回值类型 函数名 参数列表 完全一致称为重写
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) // 父类的引用/地址 class Animal *const animal
{
animal.speak();
}
/*
多态满足条件:
1、有继承关系
2、子类重写父类中的虚函数
多态使用:
父类指针或引用指向子类对象地址
*/
void test01()
{
Cat cat;
DoSpeak(cat); // 小猫在说话 如果不使用virtual关键字修饰父类的speak函数,那么这里和下面执行的都是父类的speak函数
Dog dog;
DoSpeak(dog); // 小狗在说话
}
int main() {
test01();
return 0;
}
class Base
{
public:
/*
纯虚函数
类中只要有一个纯虚函数就称为抽象类
抽象类无法实例化对象
子类必须重写父类中的纯虚函数,否则也属于抽象类
*/
virtual void func() = 0;
};
class Son :public Base
{
public:
virtual void func()
{
cout << "func调用" << endl;
};
};
void test01()
{
Base * base = NULL; // Base 类对象的指针,并将其初始化为 NULL。在 C++ 中,这通常被用作将指针初始化为空值的惯用做法。
//base = new Base; // 错误,抽象类无法实例化对象
base = new Son;
base->func();
delete base; // 记得销毁
}
int main() {
test01();
return 0;
}
通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏。怎么解决?给基类增加一个虚析构函数。虚析构函数就是用来解决通过父类指针释放子类对象
class Animal {
public:
Animal()
{
cout << "Animal 构造函数调用!" << endl;
}
/* 纯虚函数 */
virtual void Speak() = 0;
/*
正常析构函数,通过堆区的父类指针释放自身内存时,不会释放子类对象
~Animal()
{
cout << "Animal虚析构函数调用!" << endl;
}
*/
/* 析构函数加上virtual关键字,变成虚析构函数,可以释放子类的堆区数据 */
virtual ~Animal()
{
cout << "Animal虚析构函数调用!" << endl;
}
};
class Cat : public Animal {
public:
/* 在堆区开辟数据 */
Cat(string name)
{
cout << "Cat构造函数调用!" << endl;
m_Name = new string(name); // 自身成员m_Name指向由堆区开辟的数据的地址
}
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 指向堆区开辟的子类Cat对象地址
animal->Speak(); // 小猫在说话!
通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
怎么解决?给基类增加一个虚析构函数
虚析构函数就是用来解决通过父类指针释放子类对象
delete animal;
}
int main() {
/*
Animal 构造函数调用!
Cat构造函数调用!
Tom小猫在说话!
Cat析构函数调用!
Animal 纯虚析构函数调用!
*/
test01();
return 0;
}
和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。
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); // 自身成员m_Name指向由堆区开辟的数据的地址
}
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 指向堆区开辟的子类Cat对象地址
animal->Speak(); // 小猫在说话!
通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
怎么解决?给基类增加一个虚析构函数
虚析构函数就是用来解决通过父类指针释放子类对象
delete animal;
}
int main() {
/*
Animal 构造函数调用!
Cat构造函数调用!
Tom小猫在说话!
Cat析构函数调用!
Animal 纯虚析构函数调用!
*/
test01();
return 0;
}
AbstractCalculator *abc = new AddCalculator
创建了一个指向 AddCalculator 对象的 AbstractCalculator 指针。这允许你通过基类指针来访问派生类的对象。由于 AbstractCalculator 类中的 getResult() 函数被声明为虚函数,因此它是多态的,在运行时将根据实际对象的类型来调用相应的函数。因此,如果你调用 abc->getResult() 的话,实际上将调用 AddCalculator 类中重写的 getResult() 函数。在使用完毕后,一定要记得释放 abc 对象以避免内存泄漏,即使用 delete abc; 来释放对应的内存空间。
C++ 中可以使用父类的指针来接收由子类创建的对象的地址。这是由于C++中的多态性和继承机制。可以实现针对基类的指针或引用来调用派生类的成员函数,这种行为被称作多态。
#include
using namespace std;
//普通实现
class Calculator {
public:
int getResult(string oper)
{
if (oper == "+") {
return m_Num1 + m_Num2;
}
else if (oper == "-") {
return m_Num1 - m_Num2;
}
else if (oper == "*") {
return m_Num1 * m_Num2;
}
//如果要提供新的运算,需要修改源码
}
public:
int m_Num1;
int m_Num2;
};
void test01()
{
//普通实现测试
Calculator c;
c.m_Num1 = 10;
c.m_Num2 = 10;
cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
}
//多态实现 父类指针指向子类对象地址
//抽象计算器类
//多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护
class AbstractCalculator
{
public:
/* 如果不加 vritual 关键字,那么都会走这个函数,结果都是0 */
virtual int getResult()
{
return 0;
}
int m_Num1;
int m_Num2;
};
//加法计算器
class AddCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 + m_Num2;
}
};
//减法计算器
class SubCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 - m_Num2;
}
};
//乘法计算器
class MulCalculator :public AbstractCalculator
{
public:
int getResult()
{
return m_Num1 * m_Num2;
}
};
void test02()
{
AbstractCalculator *abc = new AddCalculator; // 创建加法计算器
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; // 20
delete abc; //用完了记得销毁
//创建减法计算器
abc = new SubCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; // 0
delete abc;
//创建乘法计算器
abc = new MulCalculator;
abc->m_Num1 = 10;
abc->m_Num2 = 10;
cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; // 100
delete abc;
}
int main() {
//test01();
test02();
return 0;
}