c++中类的三大特性是:继承,封装,多态。 因为近期写代码用到了类的多态性,所以在这里再总结一下。
一、多态的定义
关于多态的定义,我是参考大佬的文章再加上自己的理解得到以下内容的,大佬链接在此。
1.1 多态的定义
多态性可以简单地概括为“一个接口,多种方法”,虽然在c++中没有接口(interface)这个关键字的存在,但是可以通过多态来实现,多态的目的就是为了接口重用。C++多态性是通过虚函数(virtual)来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。override要求函数名和参数列表和父类一致。override关键字是c++11之后加进去的,目的是确保在派生类中声明的重载函数跟基类的虚函数有相同的签名。example。
1.2 override和overload的区别
另外一个在中文意思上容易与此混淆的c++方法叫做重载(overload),overload则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。只有override+virtual才算是多态,overload并不算是多态性。从编译器角度来看,override+virtual实现的函数在预编译的时候只会生成一个预处理函数,但是overload会根据各种情况生成不同的预处理函数。
1.3 多态与非多态的实质性区别
多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。多态最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C++多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。
二、多态的例子
talk is cheap , show me the code.
/*
*** in Parent.h file
*/
using namespace std;
class Parent
{
public:
virtual ~Parent();
/// <summary>
/// function.
/// </summary>
virtual void initialize() = 0;
inline const vector<pair<int, wstring> getNames() { return mNames; }
inline const vector<wstring> getResources() { return mResources; }
protected:
vector<pair<int, wstring>> mNames;
vector<wstring> mResources;
};
以上code是一个基类的声明,在基类的实现文件中就写一个析构函数即可。这里只有析构函数被声明为了虚函数,那构造函数能不能声明为虚函数呢?答案是不能。
**原因:**虚函数的实现是通过对象内存中的vptr来实现的。而构造函数是用来实例化一个对象的,通俗来讲就是为对象内存中的值做初始化操作。那么在构造函数完成之前,也即还没有进行初始化,此时vptr是没有值的,也就无法通过vptr找到作为构造函数和虚函数所在的代码区,所以构造函数只能以普通函数的形式存放在类所指定的代码区中。而对于析构函数,当我们delete(a)的时候,如果析构函数不是虚函数,那么调用的将会是基类base的析构函数。而当继承的时候,通常派生类会在基类的基础上定义自己的成员,此时我们当然希望可以调用派生类的析构函数对新定义的成员也进行析构。
上面的code中还涉及到了一个概念,就是纯虚函数virtual void initialize() = 0;
,纯虚函数也是虚函数,它的区别在于声明的时候赋值为0,纯虚函数告诉编译器要求子类必须overide这个函数才能编译通过,正常虚函数的话即使子类没有override这个虚函数也是可以编译通过的。
接下来来看子类的实现:
/*
*** in Child.h file
*/
class Child: public Parent
{
public:
/// <summary>
/// function
/// </summary>
virtual void initialize() override;
};
在子类的声明中,虽然虚函数的参数列表和返回类型都与父类一致,但是后面加上override是为了保证出现一些不必要的失误,达不到override的需求。
/*
*** in Child.cpp file
*/
void Child::initialize()
{
vector<pair<int, FontName>>().swap(mNames);
vector<wstring>().swap(mResources);
for(int i = 0 ; i < 2;++i)
{
mResources.emplace_back(i >= 1 ? L"Mother" : L"Dad");
mNames.emplace_back(i >= 1 ? L"Xiaofang" : L"Xiaoming");
}
}
在子类的定义中,实现了parent这个类的虚函数。
调用过程如下:
/*
***in main.cpp
*/
auto mPeople = std::make_unique<Child>();
mPeople->initialize();//调用子类child的方法
auto& names = mPeople->getNames();
auto& resources = mPeople->getResources();
这样就可以实现类的多态。因为override是c++11新加入的关键字,所以这篇博客就说的是c++11。
以上。