1.多态性的概念
多态性指的是同一种操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在C++中,多态性通常通过虚函数来实现,它允许我们在基类中声明一个函数为虚函数,然后在派生类中对该函数进行重写(Override)。当我们使用基类指针或引用指向派生类对象并调用虚函数时,会根据实际对象的类型来调用相应的函数版本,从而实现多态性。
2.多态的构成条件
2.1多态的构成条件
- 必须通过基类的指针或者引用调用虚函数
- 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
2.2虚函数
虚函数:即被virtual修饰的类成员函数称为虚函数。
2.3虚函数的重写
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的 返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数
#include <iostream>
// 基类
class Base {
public:
virtual void show() { // 虚函数声明
std::cout << "Base class show() called." << std::endl;
}
};
// 派生类
class Derived : public Base {
public:
void show() override { // 虚函数重写
std::cout << "Derived class show() called." << std::endl;
}
};
int main() {
Base* basePtr = new Derived(); // 基类指针指向派生类对象
basePtr->show(); // 通过基类指针调用虚函数,实现多态
delete basePtr; // 释放内存
return 0;
}
2.4虚函数重写的两个例外
- 协变
定义:协变是指基类与派生类虚函数返回值类型不同的情况。具体来说,当基类虚函数返回基类对象的指针或引用,而派生类虚函数返回派生类对象的指针或引用时,这种现象称为协变。
条件:
- 基类虚函数返回的是基类对象的指针或引用。
- 派生类虚函数返回的是派生类对象的指针或引用,且与基类虚函数具有相同的函数名、参数列表。
示例
class Base { public: virtual Base* fun() { /* ... */ } }; class Derived : public Base { public: virtual Derived* fun() override { /* ... */ } // 协变示例 };
在这个例子中,
Derived::fun()
重写了Base::fun()
,并且返回类型从Base*
协变为Derived*
- 析构函数的重写
定义:如果基类的析构函数被声明为虚函数,那么无论派生类的析构函数是否加
virtual
关键字,它都会与基类的析构函数构成重写关系,尽管它们的函数名不同。特殊处理:编译器对析构函数的名称做了特殊处理,在编译后析构函数的名称会被统一处理成
destructor
,这样即便函数名在源代码层面不同,也会构成重写。重要性:析构函数的重写对于多态性至关重要。当使用基类指针或引用删除派生类对象时,如果基类的析构函数不是虚函数,就可能不会调用派生类的析构函数,导致资源泄露或其他未定义行为。通过将基类的析构函数声明为虚函数,可以确保在删除派生类对象时正确调用其析构函数。
示例:
class Person { public: virtual ~Person() { /* ... */ } }; class Student : public Person { public: ~Student() override { /* ... */ } // 析构函数重写示例 };
在这个例子中,
Student
类的析构函数重写了Person
类的析构函数。注意,在 C++11 及以后的版本中,可以使用override
关键字来显式指示重写意图,这有助于编译器检查代码的正确性
3.抽象类
在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口 类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生 类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
4.多态的原理
4.1虚函数表
#include<iostream>
using namespace std;
class Base
{
public:
virtual void Func1()
{
cout << "Func1()" << endl;
}
private:
int _b = 1;
};
int main()
{
Base d;
cout << sizeof(d) << endl; //结果为8
return 0;
}
除了_b成员,还多一个__vfptr放在对象的前面,对象中的这个指针我们叫做虚函数表指针(v代 表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。
4.2多态的原理
class Person {
public:
virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{
p.BuyTicket();
}
int main()
{
Person Mike;
Func(Mike);
Student Johnson;
Func(Johnson);
return 0;
}