【C++grammar】多态、联编、虚函数

目录

  • 1、多态概念
    • 1.多态性有两种表现的方式
  • 2、联编(实现多态)
    • 1.静态联编
    • 2.动态联编
  • 3、实现运行时多态
    • 1.为何要使用运行时多态?
    • 2.如何实现运行时多态
    • 3.多态的例子
      • 1.调用哪个同名虚函数?
      • 2. 用途:可以用父类指针访问子类对象成员
    • 4. 虚函数的传递性
      • 虚函数的缺点:
  • 4、代码示例
    • 1、继承与重载函数的例子
      • 任务1、2
      • 任务3
    • 2、使用运行时多态
    • 3、override覆写
  • 6、运行时多态的总结
    • 1.Summary: static binding v.s. dynamic binding
    • 2. Summary: 静态联编的简单示例
    • 3. Summary: 通过基类指针访问同名函数的示例
    • 4. Summary:动态联编的简单示例-指针形式
    • 5. Summary:动态联编的简单示例-引用形式
  • 7、C++11:使用override和final
    • 1. override显式声明覆写
    • 2. final 显式声明禁止覆写

1、多态概念

广义的多态:不同类型的实体/对象对于同一消息(可以理解为同一个名字的函数)有不同的响应,就是OOP中的多态性。

1.多态性有两种表现的方式

1. 重载多态:调用参数不同的同名函数,表现出不同的行为

class C {
public: int f(int x);int f( ); 
};

2.子类型多态:不同的对象调用同名重定义函数,表现出不同的行为

class A           { virtual int f() {return 1;} };
class B: public A { virtual int f() {return 8;} };
A  a; B b;
A* p = &b;	//p虽然是a类型,但是它指向B类型的对象
a.f()   // call A::f()
b.f()   // call B::f()
p->f(); // call B::f()
//a和p是同类型,但是调用同名函数的响应是不一样的。

2、联编(实现多态)

确定具有多态性的语句调用哪个函数的过程称为联编。
在这里插入图片描述

1.静态联编

静态联编在程序编译时(Compile-time)确定调用哪个函数。
如:函数重载

2.动态联编

在程序运行时(Run-time),才能够确定调用哪个函数
用动态联编实现的多态,也称为运行时多态(Run-time Polymorphism)。

3、实现运行时多态

1.为何要使用运行时多态?

我们有三个类ABC,继承链如下:
A<-B<-C;
我们想使用print()调用toString()输出信息,就需要写三个重载函数:

void print(A obj);
void print(B obj);
void print(C obj);

这样比较麻烦。
我们可以使用运行时多态解决这个问题:

2.如何实现运行时多态

实现运行时多态有两个要素:

(1) virtual function (虚函数)
(2) Override (覆写) : redefining a virtual function in a derived class. (在派生类中重定义一个虚函数)

在正常的函数名字前加上virtual关键字,这个函数就会变为虚函数:

struct A{virtual std::string toString(){return "A";}
};

覆写是在派生类中定义一个与基类虚函数同名,参数一样,返回值一样的函数。
注意这里我们传递的是ABC类型的指针:
在这里插入图片描述

3.多态的例子

1.调用哪个同名虚函数?

(1) 不由指针类型决定;

(2) 而由指针所指的【实际对象】的类型决定

(3) 运行时,检查指针所指对象类型

2. 用途:可以用父类指针访问子类对象成员

注意print函数的参数是基类类型的指针。
然后将ABC类型的对象的地址作为函数参数传入。
后面两个print函数指针会做隐式类型转换。将派生类的地址转化为基类类型的指针,这个转换是安全的。
返回 的结果是不一样的,是和对象的实际情况相关的。
在这里插入图片描述

4. 虚函数的传递性

基类定义了虚同名函数,那么派生类中的同名函数自动变为虚函数。
注意:只需要在继承连上最顶端的基类上加上关键字即可:
在这里插入图片描述

虚函数的缺点:

1、类中保存着一个Virtual function table (虚函数表)。
2、运行时联编/动态联编,会有额外的逻辑。
所以调用虚函数比非虚函数开销大

4、代码示例

1、继承与重载函数的例子

任务1、2

1、创建A/B/C三个类,B继承A,C继承B,ABC均有toString函数
2、创建print函数,接受A类型的参数,调用A对象的toString()

不管传进来什么类型,都是打印基类类型的数据

//本部分要展示的内容如下:
//1、创建A/B/C三个类,B继承A,C继承B,ABC均有toString函数
//2、创建print函数,接受A类型的参数,调用A对象的toString()
//3、重载print函数,接受B/C类型参数,调用toStirng()#include<iostream>
#include<string>
using std::cout;
using std::endl;
class A {
public:std::string toString() { return "A"; }
};
class B : public A {
public:std::string toString() { return "B"; }
};
class C : public B {
public:std::string toString() { return "C"; }
};void print(A a) {cout << a.toString() << endl;
}
int main()
{A a;B b;C c;print(a);print(b);print(c);
}

在这里插入图片描述

任务3

重载print函数,接受B/C类型参数,调用toStirng()

//本部分要展示的内容如下:
//1、创建A/B/C三个类,B继承A,C继承B,ABC均有toString函数
//2、创建print函数,接受A类型的参数,调用A对象的toString()
//3、重载print函数,接受B/C类型参数,调用toStirng()#include<iostream>
#include<string>
using std::cout;
using std::endl;
class A {
public:std::string toString() { return "A"; }
};
class B : public A {
public:std::string toString() { return "B"; }
};
class C : public B {
public:std::string toString() { return "C"; }
};void print(A a) {cout << a.toString() << endl;
}
void print(B b) {cout << b.toString() << endl;
}
void print(C c) {cout << c.toString() << endl;
}
int main()
{A a;B b;C c;print(a);print(b);print(c);
}

在这里插入图片描述
通过三个重载函数,将打印的数据与类型匹配。

2、使用运行时多态

本部分要展示的内容如下:
1、将基类A的toStirng函数改为虚函数
2、将print函数参数改为基类指针类型
main()中调用print(),实参为指向对象的基类指针
3、添加一个print函数,参数是基类引用类型
在main()中调用print(),参数为对象的基类引用

//展示运行时多态的实现
#include<iostream>
#include<string>
using std::cout;
using std::endl;
class A {
public:virtual std::string toString() { return "A"; }
};
class B : public A {
public:std::string toString() { return "B"; }
};
class C : public B {
public:std::string toString() { return "C"; }
};void print(A* a) {cout << a->toString() << endl;
}
void print(A& a) {cout << a.toString() << endl;
}
int main()
{A a;B b;C c;A* p1 = &a;A* p2 = &b;A* p3 = &c;print(p1);print(p2);print(p3);print(a);print(b);print(c);
}

在这里插入图片描述
可以发现参数是基类引用类型与参数是基类引用类型实现的效果是一样的。这些就是多态的具体展示。
注意如果在print函数中没有找到该对象的同名函数,那么就会顺着继承链,直到在基类中找到这个同名函数。
也就是说,在继承链中如果某个类的虚函数名字写错了就会出现许多BUG。

3、override覆写

为了解决上面的问题,c++提供了覆写操作。这在第7大点将详细讲述:
在这里插入图片描述
表明了对基类函数的覆写,如果函数名字或者参数或者返回值不一样,编译器就会报错,提醒程序员。

6、运行时多态的总结

1.Summary: static binding v.s. dynamic binding

基类与派生类中有同名函数
(1) 通过派生类对象访问同名函数,是静态联编
(2) 通过基类对象的指针访问同名函数,是静态联编
(3) 通过基类对象的指针或引用访问同名虚函数,是动态联编

2. Summary: 静态联编的简单示例

class P           { public: f(){} }; //父类
class C: public P { public: f(){} }; //子类
main () {P p;   C c;p.f();         //调用P::f()c.f();         //调用C::f()
}

说明: 对象是什么类型,就调什么类型

3. Summary: 通过基类指针访问同名函数的示例

class P           { public: f(){} }; //父类
class C: public P { public: f(){} }; //子类
main () {P* ptr;   P p;   C c;ptr = &p;ptr->f();      // 调用P::f()ptr=&c;ptr->f();      // 调用P::f()
}

说明: 指针是什么类型,就调什么类型

4. Summary:动态联编的简单示例-指针形式

class P           { public: virtual f(){} }; //父类
class C: public P { public: f(){} }; //子类,f自动virtual
main () {P* ptr;   P p;   C c;ptr = &p;ptr->f();    //调用P::f()ptr=&c;ptr->f();    //调用C::f()
}

说明: 函数虚,不看指针看真对象

5. Summary:动态联编的简单示例-引用形式

class P           { public: virtual f(){} }; //父类
class C:public P { public: f(){} }; //子类,f自动virtual
main () {P p;   C c;P& pr1 = p;pr1.f();      //调用P::f()P& pr2 = c;pr2.f();      //调用C::f()
}

说明: 函数虚,不看引用看真对象

7、C++11:使用override和final

1. override显式声明覆写

C++11引入override标识符,指定一个虚函数覆写另一个虚函数。

class A {
public:virtual void foo() {}void bar() {}
};
class B : public A {
public://此处foo为常函数,与基类的foo函数不是同名覆写函数void foo() const override { // 错误: B::foo 不覆写 A::foo}                           // (签名不匹配)void foo() override;   // OK : B::foo 覆写 A::foovoid bar() override {} // 错误: A::bar 非虚
};
void B::foo() override {// 错误: override只能放到类内使用
}

override的价值在于:避免程序员在覆写时错命名或无虚函数导致隐藏bug
summary:
1、override标识符应该写到派生类同名虚函数的后面
2、非虚函数不能覆写
3、覆写只能在类的内部使用

2. final 显式声明禁止覆写

C++11引入final特殊标识符,指定派生类不能覆写虚函数。

struct Base {virtual void foo();
};struct A : Base 
{ void foo() final; // A::foo 被覆写且是最终覆写void bar() final; // 错误:非虚函数不能被覆写或是 final
};
struct B final : A // struct B 为 final,不能被继承
{void foo() override; // 错误: foo 不能被覆写,因为它在 A 中是 final
};

final标识符的作用:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377714.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一 MVC - HtmlHelper

HtmlHelper类位于System.Web.Mvc.Html之中主要有七个静态类组成&#xff1a; FormExtensions - BeginForm, BeginRouteForm, EndForm InputExtensions - CheckBox, CheckBoxFor, Hidden, HiddenFor, Password, PasswordFor, RadioButton, RadioButtonFor, TextBox, TextBoxFor …

二、用户登录和注册

一、页面设计 一共四个页面 主页面Form1&#xff0c;登录页面login&#xff0c;注册页面resister&#xff0c;主菜单页面main_page 系统运行进入Form1&#xff0c;单击登录按钮跳转到login&#xff0c;数据库中得存在数据信息且输入正确才可登录成功&#xff0c;跳转到main_pa…

【C++grammar】访问控制与抽象类与纯虚函数

目录一、访问控制 (可见性控制)1.private、public、protected关键字2.关键字示例1、关键字对类数据成员访问的限制3. 公有继承4. 私有继承5. 保护继承6. 私有继承和保护继承的区别二、抽象类与纯虚函数1.什么是抽象类2.抽象函数/纯虚函数3.抽象类示例一、访问控制 (可见性控制)…

三、上传织物图片至SQL Server并提供name进行展示织物照片

一、数据库的建立 还是在fiber_yy数据库下创建images表 images表设计如下 二、页面完善设计 main_page页面进行功能完善 入库管理系统 warehousing页面 库存查询系统 query页面 登录注册页面前面几个博文已经实现过了&#xff0c;这里就再赘述了&#xff0c;仍是沿用前…

ARM MMU工作原理剖析[转]

一、MMU的产生 许多年以前&#xff0c;当人们还在使用DOS或是更古老的操作系统的时候&#xff0c;计算机的内存还非常小&#xff0c;一般都是以K为单位进行计算&#xff0c;相应的&#xff0c;当时的程序规模也不大&#xff0c;所以内存容量虽然小&#xff0c;但还是可以容纳当…

【原创】SharePoint Document library List Check out 文档时碰到的问题解决

环境&#xff1a;TFS(Team Foundation Server)集成的WSS 3.0&#xff08;SharePoint Service 3.0&#xff09; 问题&#xff1a;如题&#xff0c;祥见下图 解决&#xff1a;一般碰到没有经验的问题&#xff0c;大家当然是外事不决问谷歌了&#xff0c;于是谷歌搜到了这篇博客 h…

四、入库管理功能的完善

一、数据库的创建 在fiber_yy数据库下创建yy_textile表 先随便添加几条数据 二、页面的完善 登录注册页面我就不演示了&#xff0c;前几篇博文也都有介绍 warehousing入库页面 main_page页面进行功能完善 三、代码实现 warehousing页面 using System; using System.…

leetcode 232. 用栈实现队列 思考分析

题目 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek() 返…

YCSB初步介绍

随着大数据时代的到来和云计算的不断发展&#xff0c;作为云计算最基础的设施存储产品也越来越多&#xff0c;开源分布式存储系统有BigTable-like系统HBase&#xff0c;dynamo-like系统Cassandra&#xff0c;voldemort&#xff0c;Riak&#xff0c;淘宝开源的OceanBase等。当然…

【C++grammar】动态类型转换、typeid与RTTI

目录动态类型转换1、为何需要动态类型转换2、dynamic_cast<>();运算符3、向上转换和向下转换( Upcasting and Downcasting)4、 基类对象和派生类对象的互操作5、Upcasting/Downcasting与继承链上不同类的对象之间的赋值有什么关系和区别&#xff1f;typeid 运行时查询类型…

五、库存查询功能的完善

一、数据库的建立 由于查询功能和之前的 入库管理功能 所用的数据库都一样&#xff0c;这里仍使用yy_textile表 在fiber_yy数据库下创建yy_textile表 初始数据库信息 二、页面的完善 登录注册页面我就不演示了&#xff0c;前几篇博文也都有介绍 query查询页面 main_page…

整合ajaxmin 和 less 到VS.net

我用的前端框架是bootstrap_extra, twitter团队做的&#xff0c;这个是他的一个扩展&#xff0c;首先从上面下载一个。至于ajaxmin&#xff0c;请参考这里1) 从bootstrap_extra的解压包中&#xff0c;复制build目录下三个文件到项目中去&#xff0c;这三个文件分别是BatchSubsi…

六、出库管理功能的实现

一、数据库的建立 这里仍使用yy_textile表 在fiber_yy数据库下创建yy_textile表 初始数据库信息 二、页面的完善 登录注册页面我就不演示了&#xff0c;前几篇博文也都有介绍 shipment出库管理页面 main_page页面进行功能完善 三、代码实现 shipment出库管理页面 u…

数学建模:层次分析法实例以及代码

博主联系方式&#xff1a; QQ:1540984562 QQ交流群&#xff1a;892023501 群里会有往届的smarters和电赛选手&#xff0c;群里也会不时分享一些有用的资料&#xff0c;有问题可以在群里多问问。 目录层次分析法的思想层次分析法步骤具体案例(市政工程项目建设决策)1.问题提出2.…

探秘IntelliJ IDEA 13测试版新功能——调试器显示本地变量

IntelliJ IDEA在业界被公认为最好的Java开发平台之一&#xff0c;JetBrains公司将在12月正式发布IntelliJ IDEA 13版本。 现在&#xff0c;小编将和大家一起探秘密IntelliJ IDEA 13测试版本新功能——调试器显示本地变量。这个功能非常强大&#xff0c;调试器可以显示变量&…

C# Windows Form下的控件的Validator(数据验证)

由于偶尔的一个想法&#xff0c;谋生了一个做一个windows form下的Validator控件&#xff0c;或者直接说类吧&#xff01; 因为webform下的Validator控件太好用了。哈哈&#xff0c;直接看代码&#xff01; 下面这个类&#xff0c;主要是一个简单的验证类&#xff0c;不过只是起…

七、流水查询---记录用户登录信息

一、数据库的建立 在fiber_yy数据库下创建yy_user_record表 可以先手动填入几条数据信息 初始数据库信息 username为用户账号 sex为用户注册所填写的性别 phone为用户手机号 time为用户登录该系统的时间 二、页面的设计 登录注册页面我就不演示了&#xff0c;前几篇博文…

leetcode 455. 分发饼干 思考分析

目录题目自己的思路以及AC代码参考思路题目 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并…

八、流水查询---记录纺织品出库信息

一、数据库的建立 在fiber_yy数据库下创建yy_textile_record表 可以先手动填入几条数据信息 初始数据库信息 第一条数据的username是空格不是null number为织物的品号(唯一的) stock为出货量 username为哪个账号 time为出货时间 二、页面的完善 登录注册页面我就不演示…

应用程序栏【WP7学习札记之九】

本节是WP7学习札记的第九篇&#xff0c;讲的是系统托盘和应用程序栏&#xff0c;具体内容是系统托盘和应用程序栏的介绍&#xff0c;如何分别使用C#、xaml以及Expression Blend生成应用程序栏&#xff0c;应用程序栏的透明度以及对屏幕方向改变的支持。摘要如下&#xff1a; 系…