更好的理解c++中的虚函数和静态多态以及动态多态

1.虚函数(Virtual Function)

在c++ 中, 虚函数是定义在基类中的函数,但是它可以在派生类中进行重写(Override) 。
通过在基类中通过virtual 关键字声明函数 , 你创建了一个可以在任何派生类中特别实现的接口。 当你有一个指针指向基类或者引用, 并通过这个指针或者引用调用虚函数时, c++ 运行时会根据指针或者引用 实际指向的对象来调用适当的函数。 这个过程是在运行时动态发生的, 这也就是所谓的动态绑定或晚绑定 。

2.多态(Polymorphism)

多态是一个通用的概念 , 指的是可以通过同一个接口访问不同类型对象的能力
在c++ 中 多态通常通过虚函数来实现, 当你使用基类的指针或者引用调用一个虚函数时, 实际上会执行派生类版本的函数, 这取决于指针或者引用实际指向的对象类型, 多态允许你编写通用的代码, 这些代码可以与任何从基类派生的类的对象一起工作, 而无需在编译时知道确切的派生类型 。

3. 例子

上面的不太好理解 : 举个简单的例子, 有一天你到餐厅吃饭,服务员给你一张菜单(基类)进行点菜,菜单上有各种食物(派生类) , 但你只需要看菜单就可以做出选择,而不需要知道每种食物是如何准备的
在c++ 中多态就相当于这个菜单 , 它允许你使用一个**通用的接口(基类的指针或引用)**来执行可能有多种不同实现的操作,这意味着我们在写代码的时候不必对象的确切类型, 只需要知道它能干什么 。
其中,虚函数就相当于菜单上的各种食物, 我们可以在基类中声明(virtual), 并且在派生类中重新定义(override)

3.1 代码实现

#include <iostream>
class Food {
public: 
	virtual void prepare()
	{
		std::cout << "prepare some food " << std::endl;
	}
};
class noodele :public Food {
public:
	void prepare () override {
		std::cout << "this is your noodel  " << std::endl;
	}
};
class water :public Food {
public:
	void prepare() override {
		std::cout << "this is your water  " << std::endl;
	}
};

void eat(Food* food)
{	//接口要是 : 基类的指针或者引用
	food->prepare(); //实现多态
}
int main()
{
	Food* myfood = new water();
	eat(myfood); 
	delete myfood; 
}

4.注意

虚函数虽然非常好用, 但是在使用虚函数的时候,并不是所有函数都得定义成虚函数,因为实现虚函数是有代价的,需要注意以下几方面的:

  1. **虚析构函数:**如果你的类有虚函数,也应该提供一个虚析构函数,为了确保通过基类指针删除派生类的对象的时候,能够调用正确的析构函数
  2. 内存和性能考虑 : 使用虚函数会增加每个对象的内存开销,以此每个对象需要一个额外的指针来访问虚函数表(v-table) ,此外,虚函数的调用比非虚函数慢,因为需要额外的间接寻址
  3. 虚函数的继承: 一旦在基类中定义了虚函数,它在派生类中自动成为虚函数, 即便没有 virtual 关键字

5. 静态多态和动态多态

  1. 静态多态中, 所有的类型检查和函数调用解析都是在编译时完成的,一次静态多态不会引入运行时的开销
  2. 动态多态 :动态动态主要是运行时进行的 主要通过虚函数实现的 ,在动态多态中, 函数的调用不是在编译时解析的,而是在运行时,当你通过基类的指针或者引用在调用一个虚函数时, c++ 运行时会根据对象的实际类型来确定调用那个函数 。

5.1 函数重载和模板

  1. 函数重载就是指你写了多个重名的函数,但是传入的参数类型或者个数不一样,编译器会根据你传入的参数类型或者参数个数来决定调用那个函数 。
  2. 模板就是一种能够让你的函数或者类能够处理不同数据类型的方法, 你写一次代码,编译器会根据你使用的具体类型来生成多个代码版本
    就比如说使用模板实现不同类型的数据交换
#include <iostream>

template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int i = 1, j = 2;
    swap(i, j);  // 交换两个int
    std::cout << "i: " << i << ", j: " << j << std::endl;

    double x = 3.14, y = 1.59;
    swap(x, y);  // 交换两个double
    std::cout << "x: " << x << ", y: " << y << std::endl;

    return 0;
}

5.2总结也就是:

静态多态的类型早已知晓 , 多态性在编译时通过函数重载和模板实现,没有运行开销
动态多态它的类型可能未知, 多态性在运行时通过虚函数实现,这涉及到类型识别,有一定的性能开销

6. 什么函数不能声明为虚函数

  • 普通函数 : 普通函数只能被overload, 不能被override,声明为虚函数也没有意义,因此编译器会在编译时绑定函数
  • 构造函数: 构造函数是为了明确初始化对象成员才产生的, 然而virtual function 主要是为了再不完全了解细节的情况下也能处理对象, 另外virtual function 函数是在不同类型的对象产生不同的对象, 现在对象还没产生,如何使用virtual 函数来完成你想完成的动作 。
  • 静态成员函数: 静态成员函数是不依赖于类的实例,而虚函数是要通过对象的虚表(vtable)来调用
  • 主函数:主函数是程序的入口点,以此不会被声明为虚函数 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值