前言:
多态是面向对象三大特征之一,也是在面试的时候问的最多的问题。
1、概念
简单来说,多态就是多种形态,当我们要完成某个行为的时候,不同的对象去完成时会产生不同的状态,这就叫做多态。多态就是同一个接口,不同功能的实现。
2、特点
多态分为两种:静态多态和动态多态。在编译时的多态,就是静态多态,在运行时的多态,就是动态多态。
2.1 静态多态
静态多态主要通过重载或者模板来实现的,就是要求在相同作用域下,函数名一样,参数列表不一样,可以是参数的类型不一样,也可以是参数数量不一样,当然给参数加const也可以实现重载,在编译的时候,通过实参来确定调用的,属于编译时多态,也就是静态多态。
代码如下:
#include <iostream>
using namespace std;
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
template <typename T>
T add(T a, T b)
{
return a + b;
}
int main()
{
cout << add(10,20) << endl; // 调用int add(int a, int b) 输出30
cout << add(1.2,2.0) << endl; // 调用double add(double a, double b) 输出3.2
cout << add<char>('A', ' ') << endl; //调用模板函数 输出'a'
cout << add<int>(5, 10) << endl; //调用模板函数 输出15
return 0;
}
重载函数在c++中还有运算符重载,之后我再补充。
2.2 动态多态
我们常说的C++的多态,默认主要说动态多态为主。动态多态最常见的用法就是声明基类的指针,使用该指针指向任意一个派生类的对象。
2.2.1 虚函数
概念:
在类中声明为 virtual的成员函数,称为虚函数。虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。
如果没有使用虚函数,基类指针只能调用基类的成员函数,不能调用派生类的成员函数。
代码:
class Base
{
public:
void output()
{
cout << "Base::output()" << endl;
}
};
class Derived : public Base
{
public:
void output()
{
cout << "Derived::output()" << endl;
}
};
int main()
{
Derived derived;
Base *base1 = &derived;
base1->output();
Base & base2 = derived; // 基类的引用也可以使用多态
base2.output();
return 0;
}
运行结果:
Base::output()
Base::output()
在基类的成员函数前加virtual关键字,把其声明为虚函数。有了虚函数,基类指针表现出了多种形态,这种现象称为多态。
代码:
class Base
{
public:
virtual void output() //成员函数声明前添加virtual关键字
{
cout << "Base::output()" << endl;
}
};
class Derived : public Base
{
public:
virtual void output() //派生类的函数不写virtual也能成为虚函数,但是为了代码清晰,建议写上
{
cout << "Derived::output()" << endl;
}
};
int main()
{
Derived derived;
Base *base1 = &derived;
base1->output();
Base & base2 = derived; // 基类的引用也可以使用多态
base2.output();
return 0;
}
运行结果:
Derived::output()
Derived::output()
2.2.2 虚析构函数
析构函数也可以定义为虚函数,如果基类的析构函数定义为虚析构函数,则派生类的析构函数就会自动成为虚析构函数。
如果基类的指针指向派生类的对象,当用delete删除这个对象时,若析构函数不是虚析构函数,只会调用基类的析构函数,不会调用派生类的析构函数,会导致内存泄漏。
代码:
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
~Base()
{
cout << "Base析构函数" << endl;
}
};
class Derived : public Base
{
public:
Derived()
{
cout << "Derived构造函数" << endl;
}
~Derived()
{
cout << "Derived析构函数" << endl;
}
};
int main()
{
Base *base = new Derived;
delete base;
return 0;
}
运行结果:
Base构造函数
Derived构造函数
Base析构函数
给基类的析构函数加上virtual关键字,使析构函数成为虚析构函数。
代码:
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
virtual ~Base() //添加virtual关键字
{
cout << "Base析构函数" << endl;
}
};
再次运行,运行结果如下:
Base构造函数
Derived构造函数
Derived析构函数
Base析构函数
2.2.3 虚函数表
c++的虚函数是通过虚函数表来实现动态多态的。表里面存放了类中的虚函数地址信息。虚函数表指针放在对象的起始位置上。
class Base
{
public:
int a;
int b;
virtual void fun1()
{
cout << "fun1" << endl;
}
virtual void fun2()
{
cout << "fun2" << endl;
}
};
代码如下:
int main()
{
Base base;
cout << "当前对象所占空间大小:" << sizeof(base) << endl;
cout << &base << endl;
cout << &base.a << endl;
cout << &base.b << endl;
return 0;
}
运行结果:
当前对象所占空间大小:12
005DF984
005DF988
005DF98C
接下来,使用函数指针来调用类中的虚函数
代码如下:
int main()
{
Base base;
typedef void(*Test)();
Test *vtaddr = (Test *)*(int*)&base;
vtaddr[0]();
vtaddr[1]();
return 0;
}
运行结果:
fun1
fun2
2.2.4 纯虚函数
纯虚函数是基类中只声明函数,没有实现,要求在派生类中定义的成员函数。
语法:
class 类名
{
访问权限:
virtual 类型 函数名(形参列表) = 0; //无函数体
};
抽象类:
1.有纯虚函数的类被称为抽象类或虚基类;
2.抽象类不能被实例化(不能被定义为对象);
3.从抽象类继承而来的派生类,可以不实现纯虚函数,如果不实现,派生类还是抽象类,也不可被实例化;
4.可以声明抽象类的指针和引用。
声明如下抽象类:
class Base
{
public:
int a;
int b;
virtual void fun1() = 0;
};
在派生类中实现纯虚函数
class Derived : public Base
{
public:
void fun1() //实现基类的纯虚函数
{
cout << "fun1()" << endl;
}
};
int main()
{
Base *base = new Derived; //可以声明抽象类的指针和引用
base->fun1();
delete base;
return 0;
}