什么是多态
父类引用或者指针指向子类对象。
编译时多态(静态连编):函数和运算符重载。编译阶段就确定了地址。
运行时多态(动态连编):通常说的多态。
多态的使用
使用条件:
- 父类中函数为虚函数
- 子类继承父类并重写该虚函数,子类virtual可写可不写
- 父类指针或引用指向子类对象,然后调用方法。
也就是说C++中如果要实现多态,函数必须为虚函数,而使用虚函数,也就是为了实现多态。尽量不要重写父类的普通函数。
而定义函数为纯虚函数是为了子类必须重写该函数。说明该类是一个接口,
静态连编
#include <iostream>
using namespace std;
class Animal
{
public:
void speak()
{
cout << "动物" << endl;
}
};
class Cat : public Animal
{
public:
//子类重写父类方法
void speak() //子类中virtual可写可不写
{
cout << "小猫" << endl;
}
};
void doSpeak(Animal &animal){
animal.speak();
}
int main()
{
Cat cat;//创建cat对象并传入方法,cat是animal的子类,所以可以认为cat是animal的一种类型
//所以可以把cat传进去
doSpeak(cat);//动物 但是这里调用的还是父类的方法,因为没有产生多态,是静态编译,只有用virtual才能产生多态
Animal &a1 = cat1;
a1.speak();//动物
Animal *a2 = new Cat;
a2->speak();//动物
}
动态连编
#include <iostream>
using namespace std;
class Animal
{
public:
void virtual speak()
{
cout << "动物" << endl;
}
};
class Cat : public Animal
{
public:
//子类重写父类方法
void speak()
{
cout << "小猫" << endl;
}
};
void doSpeak(Animal &animal){
animal.speak();
}
int main()
{
Cat cat1;
doSpeak(cat1);//小猫 使用了virtual所以为动态连编,为多态
Animal &a1 = cat1;
a1.speak();//小猫
Animal *a2 = new Cat;
a2->speak();//小猫
}
多态的好处
Animal *a2 = new Cat;
a2->speak();//小猫
比如这句话,我们可以根据我们的需要来创建不同对象,我们可以创建小猫,小狗,小猪,然后把它给animal。然后animal调用speak就可以实现多态,也就是根据创建的不同对象调用不同对象中的speak方法。“一个接口,多种方法”,程序在运行时才决定调用的函数。接口重用。
代码的原则:修改关闭,扩展开放。
计算器例子
#include <iostream>
using namespace std;
class Calculator
{
public:
int v1;
int v2;
void setv1(int v)
{
v1 = v;
}
void setv2(int v)
{
v2 = v;
}
int virtual getResult()
{
return v1 + v2;
}
};
class PlusCalculator : public Calculator
{
int virtual getResult()
{
return v1 + v2;
}
};
class SubCalculator : public Calculator
{
int virtual getResult()
{
return v1 - v2;
}
};
int main()
{
Calculator *calculator;
//使用加法计算器
calculator = new PlusCalculator;
calculator->setv1(10);
calculator->setv2(20);
cout << calculator->getResult() << endl; //30
delete calculator; //清除
//使用减法计算器
calculator = new SubCalculator;
calculator->setv1(10);
calculator->setv2(20);
cout << calculator->getResult() << endl;
}
如果我们需要继续扩展添加乘法计算器,就直接添加,使用的方式和上面一样,扩展非常方便。这就是多态的好处。结构性好,可读性高。
C++比C语言效率低,其实就是在多态上,因为内部结构复杂了,就比c语言低一点点。
纯虚函数和抽象类
接着上面的例子,我们可以把父类的函数作为一个纯虚函数。作用和上面一样实现多态。
- 当一个类有纯虚函数时,这个类就为抽象类,不能实例化。相当于一个接口,只做多态使用而不能创建该类对象。
- 子类必须重写实现所有纯虚函数,不然子类也是一个抽象类,不能实例化。
class Calculator
{
public:
int v1;
int v2;
void setv1(int v)
{
v1 = v;
}
void setv2(int v)
{
v2 = v;
}
//纯虚函数:子类必须重写此函数
//这个类为抽象类,该类无法实例化;
int virtual getResult() = 0;
};
虚析构和纯虚析构
当我们继承的时候,父类指针指向子类对象,然后释放父类,则只创建了子类对象,而没有释放子类对象。这样会导致释放不干净内存问题。
#include <iostream>
using namespace std;
class Calculator
{
public:
Calculator()
{
cout << "计算器构造" << endl;
}
~Calculator()
{
cout << "计算器析构" << endl;
}
};
class PlusCalculator : public Calculator
{
public:
PlusCalculator()
{
cout << "加法计算器构造" << endl;
}
~PlusCalculator()
{
cout << "加法计算器析构" << endl;
}
};
int main()
{
Calculator *calculator;
//使用加法计算器
calculator = new PlusCalculator;
delete calculator;
}
/*
计算器构造
加法计算器构造
计算器析构
*/
解决问题:使用虚析构
#include <iostream>
using namespace std;
class Calculator
{
public:
Calculator()
{
cout << "计算器构造" << endl;
}
virtual ~Calculator()
{
cout << "计算器析构" << endl;
}
};
class PlusCalculator : public Calculator
{
public:
PlusCalculator()
{
cout << "加法计算器构造" << endl;
}
virtual ~PlusCalculator()
{
cout << "加法计算器析构" << endl;
}
};
int main()
{
Calculator *calculator;
//使用加法计算器
calculator = new PlusCalculator;
delete calculator;
}
/*
计算器构造
加法计算器构造
加法计算器析构
计算器析构
*/
虚析构也可以定义为纯虚析构,但是需要类内声明,类外必须实现,如果析构函数为纯虚析构,则该类为抽象类,不能实例化。
也就是说如果析构函数是纯虚析构则该类肯定为抽象类。
向上转型和向下转型
向下转型
父类转为子类,叫做向下转型,是不安全的。
因为子类东西多,父类东西少,父类转为子类是不安全的。
Animal *animal = new Animal;
Cat *cat = (Cat*) animal; // 把父类转为子类
向上转型
子类转为父类,是安全的,向上转型,父类东西少,是安全的。
Cat *cat = new Cat;
Animal *animal = (Animal*) cat; // 把父类转为子类
发生了多态
如果发生类多态,则向下转换也是安全的。
Animal *animal = new Cat;
Cat *cat = (Cat*) animal;