多态性和虚函数

C++中有三大特性:抽象性,继承性,多态性,今天我们主讲多态性的使用。

多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作。多态性是指具有相似功能的不同函数使用的统一名称,从而使得相同的调用方式达到调用具有不同功能的同名函数的效用。也就是所谓的**“一个接口多个方法”**,
补充个概念:
静态联编和动态联编:

  1. 静态联编:在一个源程序经过编译连接成为可执行文件的这个过程中的编译阶段完成的,也就是说静态联编是在程序运行之前完成的;
  2. 动态联编:在程序运行时才动态完成的。
    静态联编在程序编译是就知道调用函数的全部信息,这种联编类型的函数调用速度快,效率高。
    编联是把一条消息和一个对象的方法相结合,这种结合是对象首先接收消息,然后把方法和消息结合在一起。

C++实现多态性有以下两种情况:
3. 编译时多态性:通过函数重载,运算符重载,模板实现
4. 运行时多态性:借助虚函数来实现。

虚函数

class shape
{
public:
	float area()
	{
		return -1;
	}
};
class circle :public shape
{
private:
	float radious;
public:
	 circle(float r)
	{
		 radious = r;
	}
	 float area()
	 {
		 return 3.14158*radious*radious;
	 }
};

我们来看这个求圆面积类,我们来看主函数

shape obj, *ptr;
	circle c(3.6);
	ptr = &obj;
	cout << ptr->area() << endl;
	ptr = &c;
	cout << ptr->area() << endl;

按理说是会出现 -1, 半径为3.6的圆的面积。但是
看结果在这里插入图片描述
为什么会出现这个情况呢?因为两次调用都是静态联编,所以都会调用到

float area()
	{
		return -1;
	}

我们只需要将这个函数改一下

virtual float area()
	{
		return -1;
	}

就能得到想要的答案了

通过这个例子我们可以看到,派生类中虚函数被重新定义实现不同的操作,这种被称为函数覆盖。

virtual 作用:
1:指示C++编译器对该函数的调用进行动态联编;
2:对象点运算符调用虚函数是静态联编过程
3:只有当访问虚函数是通过基类指针才获得运行时多态性
对于虚函数:
基类定义中必须把成员函数定义为虚函数
派生类中,虚函数被重新定义时,函数原型与基类中的函数原型必须完全相同
虚函数声明只出现在类声明的函数原型声明中,在派生类中可以以不显示的声明为虚函数
必须使用基类的指针(或者引用)访问虚函数,此时多态性才能实现
虚函数必须在继承体系下实现
虚函数必须是其所在类的成员函数
虚函数不能是友元函数
虚函数不能是静态成员函数
内联函数不能是虚函数,因为虚函数在类的内部定义,在编译时仍然看做是内联的
构造函数不能是虚函数但是析构函数可以是虚函数
一个虚函数无论被继承多少次仍保持虚函数特性

虚函数和重载函数的关系

重载函数要求函数有相同的函数名称,并有不同的参数序列;虚函数要求(函数名称,返回值参数序列)完全相同,具有完全相同的函数原型,
重载函数可以是成员函数和非成员函数,虚函数只能是成员函数
重载函数的调用那个是以所传递参数序列的差别(参数个数或者类型的不同)作为调用不同函数的依据,虚函数是根绝对象的不同调用不同类的虚函数。

纯虚函数和抽象类:
语法:
纯虚函数
virtual 函数类型 函数名称(形式参数表) = 0;
纯虚函数没有函数体,当声明为纯虚函数,基类就不给函数的实现部分,函数体由基类的派生类给出。
抽象类:
1:抽象类至少包含一个没有定义功能的纯虚函数,抽象类智能用作派生类的基类,不能建立抽象类;
2:抽象类不能用作参数类型,函数返回类型;
3:如果在抽象类的派生类没有重新说明纯虚函数,派生类只是继承基类的纯虚函数,这个派生类仍然是个抽象类。

<think>好的,我现在需要处理用户关于C++多态性虚函数的问题。用户希望得到详细的解释使用方法。首先,我得回忆一下相关的知识点,并结合提供的引用内容来组织回答。 用户提供的引用内容提到了虚函数的作用、多态性的实现机制,以及虚函数的底层实现。引用[1][2]都强调了虚函数如何实现动态绑定,允许基类指针调用子类方法。引用[3]给出了一个代码示例,说明基类派生类中虚函数的重写以及函数重载的情况。这些内容都是回答用户问题的重要参考。 首先,我需要明确多态性的基本概念,分为编译时多态运行时多态。编译时多态包括函数重载模板,而运行时多态则通过虚函数继承实现。用户的问题重点在运行时多态,也就是虚函数的使用。 接下来,需要解释虚函数的定义方法,即使用virtual关键字,并在派生类中重写。同时,要提到如果派生类没有重写虚函数,则会继承基类的版本。这点在引用[2]中有提到,虚属性可以被继承。 然后,动态绑定的机制是关键。需要说明基类指针或引用如何指向派生类对象,并调用相应的虚函数。这部分可以结合引用[1]中的描述,用简单的例子来说明。例如,基类Animal派生类Dog,通过基类指针调用虚函数speak(),实际执行的是Dog类的实现。 另外,纯虚函数抽象类的概念也需要涵盖。用户可能需要了解如何定义接口,强制派生类实现特定方法。引用中没有直接提到纯虚函数,但这是多态性中的重要部分,需要补充。 虚析构函数的问题也不能忽视。如果基类的析构函数不是虚的,当通过基类指针删除派生类对象时,可能导致资源泄漏。引用中没有提到这一点,但这是使用虚函数时的常见注意事项,应该包含在回答中。 引用[3]中的代码示例展示了函数重载虚函数重写的区别。在派生类中,即使基类的函数虚函数,重载的函数(如func(int))并不会自动成为虚函数,除非也使用virtual关键字声明。这可能是一个容易混淆的地方,需要解释清楚。 此外,虚函数的底层实现,如虚函数表(vtable)虚函数指针(vptr),可以简要提及,以帮助理解动态绑定的机制。这部分在引用[1]的末尾提到,可以适当展开。 用户可能还关心虚函数的使用场景注意事项,比如性能影响(虚函数调用有间接开销)、设计模式中的应用(如工厂模式、策略模式)等。这些内容可以增强回答的实用性。 最后,按照用户要求生成相关问题,需要围绕多态性虚函数的使用细节、设计考虑等方面提出,例如虚函数与普通成员函数的区别,虚函数表的工作原理,什么情况下必须使用虚函数等。 现在需要将以上思路组织成结构清晰的中文回答,确保符合格式要求:行内公式用$...$,独立公式用$$...$$,正确使用LaTeX语法,引用标识自然添加,并在最后生成相关问题。同时,回答需要分点说明,结合示例代码,帮助用户逐步理解。</think>### C++多态性虚函数详解 #### 一、多态性基础概念 多态性分为两种类型: 1. **编译时多态**:通过函数重载模板实现,例如: ```cpp int add(int a, int b) { return a+b; } double add(double a, double b) { return a+b; } ``` 2. **运行时多态**:通过虚函数继承实现,满足$Liskov$替换原则[^1] #### 二、虚函数核心机制 1. **定义方式** ```cpp class Animal { public: virtual void speak() { // 基类虚函数 cout << "Animal sound" << endl; } }; class Dog : public Animal { public: void speak() override { // 派生类重写 cout << "Woof!" << endl; } }; ``` 2. **动态绑定示例** ```cpp Animal* animal = new Dog(); animal->speak(); // 输出"Woof!" [^2] ``` 3. **虚函数表(vtable)** - 每个包含虚函数的类自动生成虚函数表 - 对象内部包含隐藏指针`vptr`指向vtable - 调用过程:`obj->vptr[n]()`(n为函数索引) #### 三、高级特性 1. **纯虚函数与抽象类** ```cpp class Shape { public: virtual double area() = 0; // 纯虚函数 }; ``` 2. **虚析构函数必要性** ```cpp class Base { public: virtual ~Base() {} // 必须声明为虚函数 }; ``` 3. **override/final关键字** ```cpp class Derived : public Base { public: void func() override {} // 显式声明重写 virtual void finalFunc() final {} }; ``` #### 四、关键应用场景 1. 插件架构设计 2. GUI事件处理系统 3. 游戏实体组件系统 4. 框架扩展点实现 ```cpp // 工厂模式示例 class Creator { public: virtual Product* create() = 0; }; class CarFactory : public Creator { public: Product* create() override { return new Car(); } }; ``` #### 五、性能与注意事项 1. 虚函数调用比普通函数多一次指针间接访问 2. 虚函数不应声明为`inline` 3. 构造函数虚函数机制未生效 4. 虚函数表占用额外内存空间 $$ \text{内存开销} = \text{sizeof(vptr)} + \text{vtable大小} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值