文章目录
- 多态
- 1.多态的概念和介绍
- 2.虚函数
- 2.1final
- 2.2override
- 3.虚函数的重写
- 3.1协变
- 3.2析构函数的重写
- 4.多态构成的条件
- 5.重载、重写、重定义
- ......
多态
1.多态的概念和介绍
C++中的多态是一种面向对象编程的特性,它允许不同的对象对同一个消息做出不同的响应。 多态性能够提高代码的可复用性和灵活性,使得代码更加模块化和可扩展。
多态性是通过使用继承和虚函数实现的。 当一个类被声明为虚函数时,它可以被子类重写,并且在运行时根据对象的实际类型来调用相应的函数。这种动态绑定的特性使得程序能够根据不同的对象类型来执行不同的操作。
简单总结:就是在完成某个行为时,不同的对象会产生出不同的状态。
通过多态我们可以模拟实现不同种动物的叫声:
#include <iostream>
using namespace std;
// 基类
class Animal {
public:
virtual void makeSound() {
cout << "Animal makes sound" << endl;
}
};
// 派生类
class Cat : public Animal {
public:
virtual void makeSound() {
cout << "meows" << endl;
}
};
class Dog : public Animal {
public:
virtual void makeSound() {
cout << "barks" << endl;
}
};
class Capybara : public Animal {
public:
virtual void makeSound() {
cout << "ka~pi~ba~la~" << endl;
}
};
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
Animal* animal3 = new Capybara();
animal1->makeSound(); // 调用Dog类的makeSound函数
animal2->makeSound(); // 调用Cat类的makeSound函数
animal3->makeSound(); // 调用Capybara类的makeSound函数
delete animal1;
delete animal2;
delete animal3;
return 0;
}
//meows
//barks
//ka~pi~ba~la~
2.虚函数
虚函数:即被virtual修饰的类成员函数称为虚函数。
虚函数是为了实现动态绑定和多态性。当基类的指针或引用指向派生类对象时,通过调用虚函数,可以根据对象的实际类型来确定调用哪个函数。这样在运行时才确定函数的调用,而不是在编译时确定。
虚函数的定义如下:
class Base {
public:
virtual void function() {
// 函数的实现
}
};
需要注意的是,虚函数可以是纯虚函数,即在基类中只有函数的声明而没有实现。 纯虚函数的定义如下:
包含纯虚函数的类被称为抽象类,抽象类不能被实例化,只能作为基类来派生其他类。派生类必须重写纯虚函数才能被实例化。
class Base {
public:
virtual void function() = 0; // 纯虚函数
};
C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,所以:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。
2.1final
final:修饰虚函数,表示该虚函数不能再被重写。
class Car
{
public:
virtual void Drive() final {}
};
class Benz :public Car
{
public:
virtual void Drive() {cout << "Benz-舒适" << endl;}
};
2.2override
override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
class Car{
public:
virtual void Drive(){}
};
class Benz :public Car {
public:
virtual void Drive() override {cout << "Benz-舒适" << endl;}
};
3.虚函数的重写
虚函数的重写是指在派生类中重新定义基类中已经声明为虚函数的函数。通过重写虚函数,派生类可以根据自己的需要重新实现该函数,从而覆盖基类中的实现。
虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同)。
下面的代码就构成重写:
// 基类
class Animal {
public:
virtual void makeSound() {
cout << "Animal makes sound" << endl;
}
};
// 派生类
class Cat : public Animal {
public:
virtual void makeSound() {
cout << "meows" << endl;
}
};
3.1协变
协变:基类与派生类虚函数返回值类型不同。
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person {
public:
virtual A* f() {return new A;}
};
class Student : public Person {
public:
virtual B* f() {return new B;}
};
3.2析构函数的重写
析构函数的重写:基类与派生类析构函数的名字不同。
如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。
析构函数可以被重写,其语法与普通函数的重写相同。派生类可以在其定义中重写基类的析构函数,以便在对象销毁时执行特定的清理操作。
下面是一个示例代码,演示了如何在派生类中重写基类的析构函数:
#include <iostream>
class Base {
public:
virtual ~Base() {
std::cout << "Base destructor" << std::endl;
}
};
class Derived : public Base {
public:
~Derived() override {
std::cout << "Derived destructor" << std::endl;
}
};
int main() {
Base* base = new Derived();
delete base;
return 0;
}
//Derived destructor
//Base destructor
4.多态构成的条件
多态性的实现需要满足以下条件:
(1)存在继承关系: 多态性是通过基类和派生类之间的继承关系来实现的。派生类继承了基类的成员函数和虚函数。
(2)使用基类指针或引用来调用派生类对象: 通过将基类指针或引用指向派生类对象,可以实现多态性。通过基类指针或引用调用虚函数时,实际调用的是派生类中重写的函数。
(3)基类中存在虚函数:在基类中声明一个虚函数,使得它可以在派生类中被重写。 通过将基类的成员函数声明为虚函数,可以实现动态绑定,即在运行时根据对象的实际类型来确定调用哪个函数。
对于上面的代码就满足虚函数的重写:
简单的多态实现的例子:
Shape类是一个基类,它包含一个虚函数draw。Circle类和Rectangle类分别是Shape类的派生类,它们重写了draw函数。
在主函数中,我们创建了一个指向Circle对象的Shape指针和一个指向Rectangle对象的Shape指针。当调用shape1->draw()时,由于shape1指向的是Circle对象,所以会调用Circle类的draw函数,输出"Drawing a circle"。
同样地,当调用shape2->draw()时,由于shape2指向的是Rectangle对象,所以会调用Rectangle类的draw函数,输出"Drawing a rectangle"。
#include <iostream>
using namespace std;
// 基类
class Shape {
public:
virtual void draw() {
cout << "Drawing a shape" << endl;
}
};
// 派生类
class Circle : public Shape {
public:
void draw() {
cout << "Drawing a circle" << endl;
}
};
class Rectangle : public Shape {
public:
void draw() {
cout << "Drawing a rectangle" << endl;
}
};
int main() {
Shape* shape1 = new Circle();
Shape* shape2 = new Rectangle();
shape1->draw(); // 调用Circle类的draw函数
shape2->draw(); // 调用Rectangle类的draw函数
delete shape1;
delete shape2;
return 0;
}
//Drawing a circle
//Drawing a rectangle
总结多态的构成条件:
(1)存在继承;
(2)必须通过基类的指针或者引用调用虚函数;
(3)被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。
5.重载、重写、重定义
重载(Overload)、重写(Override)和重定义(Hide)是面向对象编程中的三个概念,用于描述派生类对基类成员的处理方式。
(1)重载(Overload): 重载是指在同一个作用域内,根据函数名相同但参数列表不同的规则,定义多个具有相同名称但参数不同的函数。 重载函数可以有不同的返回类型,但不能仅通过返回类型的不同来进行重载。在调用重载函数时,编译器会根据函数调用的参数类型和数量来确定调用哪个重载函数。
(2)重写(Override): 重写是指在派生类中重新定义基类的虚函数。派生类中的重写函数具有相同的函数名、参数列表和返回类型,它们用于替代基类中的虚函数。 在运行时,通过基类指针或引用调用虚函数时,会根据实际对象的类型调用相应的派生类的重写函数。重写函数可以通过使用override关键字来显式地标记。
(3)重定义(Hide): 重定义是指在派生类中定义一个与基类中的非虚函数同名的函数。 在派生类中的重定义函数与基类中的函数没有关联,它们是完全独立的函数。在运行时,通过基类指针或引用调用函数时,会根据指针或引用的类型调用相应的函数。重定义函数不需要使用特定的关键字进行标记。
…
当然多态还有很多的知识点,这里只是对C++多态的部分介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉