多态使得不同类型的对象可以通过相同的接口进行交互,而每个对象的具体行为是根据其自身类型来决定的,分为编译时多态和运行时多态。举个例子:对于普通人来说,票价全价,学生来说,票价半价,军人来说,优先买票等,指在根据不同的对象完成不同买票行为。 下面用示例代码来展现该行为(如下):
#include <iostream>
using namespace std;
class Person
{
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-打折" << endl; }
};
void Func(Person* ptr)
{
// 这里可以看到虽然都是Person指针Ptr在调用BuyTicket
// 但是跟ptr没关系,而是由ptr指向的对象决定的。
ptr->BuyTicket();
}
int main()
{
Person p1;
Student s1;
Func(&p1);
Func(&s1);
return 0;
}结果如下:

通过传递不同的对象,来实现不同的行为,上述传递Person类和Student类,分别对应的行为是全价和半价。
多态的实现原理通过虚函数表,虚表由虚函数构成,即 Virtual 修饰的函数,还需要虚表指针进行定位函数调用。
注意:必须是成员函数,不是成员函数的报错示例:

error: 类声明的外部说明符无效。
定义:类成员函数被virtual修饰,这个成员函数被称为虚函数。
定义:派生类有一个与基类完成相同的虚函数(即派生类函数与基类函数的返回值类型、函数名字,参数列表完全形同,缺一不可),称为派生类的虚函数重写了基类的虚函数。注意:派生类的虚函数不加 virtual 修饰,也构成重写,但是不规范,不建议使用。
class A
{
public:
virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
virtual void test(){ func();}
};
class B : public A
{
public:
void func(int val = 0){ std::cout<<"B->"<< val <<std::endl; }
};
int main(int argc ,char* argv[])
{
B*p = new B;
p->test();
return 0;
}以下程序输出结果是什么() A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确
调用func前面是A*类型,是基类的指针调用,派生类对该函数进行了重写,所以构成多态。指向派生类,所以调用派生类的func,而派生类B前面省略了virtual,编译器会将派生类调用的func修改成:virtual void func(int val = 1){ std::cout<<“B->”<< val <std::endl;}所以叫作对基类虚函数的重写。即B-1,所以答案是B。
派生类重写基类虚函数时,与基类虚函数返回值类型不同时。简单点说就是:基类指针返回基类对象的指针或引用,派生类返回派生类对象的指针或引用,称为协变。
class A {};
class B : public A {};
class Person {
public:
virtual A* BuyTicket()//返回基类指针对象
{
cout << "买票-全价" << endl;
return nullptr;
}
};
class Student : public Person {
public:
virtual B* BuyTicket()//返回派生类指针对象
{
cout << "买票-打折" << endl;
return nullptr;
}
};
void Func(Person* ptr)
{
ptr->BuyTicket();
}
int main()
{
Person ps;
Student st;
Func(&ps);
Func(&st);
return 0;
}上述举了一个关于协变的例子。
注意:需将对基类的析构函数进行重写。下面给个代码例子来解释:
class A
{
public:
virtual ~A()
{
cout << "~A()" << endl;
}
};
class B : public A {
public:
virtual ~B() override
{
cout << "~B()->delete:" << _p << endl;
delete [] _p;
}
protected:
int* _p = new int[10];
};
int main()
{
A* p1 = new A;
A* p2 = new B; //这是C++的静态绑定特性:非虚函数调用仅由指针的静态类型(此处为A * )决定。
delete p1;
delete p2;
return 0;
}注意:C++的静态绑定特性:非虚函数调用仅由指针的静态类型决定。当基类的析构函数没有被修饰为虚函数,进行静态绑定,非虚函数调用仅由指针的静态类型(A*)决定。下面的B对象的资源不会被释放,造成内存泄漏。。当基类的析构函数被修饰为虚函数,构成多态,运行时绑定p2指针指向B对象,调用B对象的析构函数,成功将B对象的资源释放。
所以建议:最好将基类的析构函数修饰为虚函数,避免内存泄漏。
class Car
{
public:
virtual void Drive() = 0;
};class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl;
}
};看看下面程序在32为程序的运行结果是什么?
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
virtual void Func2()
{
cout << "Func2()" << endl;
}
void Func3()
{
cout << "Func3()" << endl;
}
protected:
int _b = 1;
char _ch = 'x';
};
int main()
{
Base b;//类内包含一张纯虚函数表,也就是函数指针数组
cout << sizeof(b) << endl;
return 0;
}输出结果为12,因为里面还存在虚函数表指针,指针在32位机器占4字节。 如图:

构成多态时,运行到指定的对象的虚表中确定对应的虚函数,不再在编译时绑定,称为静态绑定,这是称为动态绑定。
本文深入探讨了C++多态性的核心概念与实现机制,涵盖多态定义、虚函数、协变、析构函数重写、override/final关键字、重载/重写/隐藏对比、纯虚函数与抽象类,以及多态原理如虚函数表指针、静态/动态绑定等,是C++面向对象编程的进阶指南。