【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 …

HDOJ 400题纪念。

刚刚交了1506&#xff0c;无意间瞟到左边的随笔数&#xff0c;发现已经401题了&#xff0c;这么说前几天就400题了啊囧。 昨天还想交到400题就先放放&#xff0c;背单词的&#xff0c;没想到那么快。等把USACO那个八皇后写完吧。人生总是有许多不想做又不得不做的事情。。。 还…

二、用户登录和注册

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

readdir函数_PHP readdir()函数与示例

readdir函数PHP readdir()函数 (PHP readdir() function) The full form of readdir is "Read Directory", the function readdir() is used to read the directory i.e. read the name of the next entry in the directory. readdir的完整形式为“ Read Directory”…

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

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

mongodb 如何删除 字段值为 json对象中的某个字段值

例如&#xff1a; { attributes: { birthday:1988-01-01, name: aq } } birthday是attributes字段的value的一个字段&#xff0c; 我要删除birthday 用这句话&#xff1a; db.User.update({email:adminlinkris.com},{$unset:{attributes.birthday:}})转载于:https://www.cnblog…

使用 Spring 的 Web 服务模拟器框架解决方案

http://www.ibm.com/developerworks/cn/web/wa-aj-simulator/index.html转载于:https://www.cnblogs.com/diyunpeng/archive/2012/02/28/2371390.html

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

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

gettype_PHP gettype()函数与示例

gettypePHP gettype()函数 (PHP gettype() function) In PHP, we have a library function gettype() to identify the type of data. The function is primarily used to sanity check the type of data being input in a variable. The function can identify the data into …

ARM MMU工作原理剖析[转]

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

栈与队列在SGI STL的底层实现

栈 栈提供push和pop等接口&#xff0c;不提供走访功能&#xff0c;也不提供迭代器。 STL中栈不被归类为容器&#xff0c;而被归类为container adapter(容器适配器)&#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…

getdate函数_PHP getdate()函数与示例

getdate函数PHP getdate()函数 (PHP getdate() function) getdate() function is used to get the local date/time (or it is also used to get the date/time based on the given timestamp. getdate()函数用于获取本地日期/时间(或也用于根据给定的时间戳获取日期/时间。 S…

四、入库管理功能的完善

一、数据库的创建 在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等。当然…

kotlin实现继承_Kotlin程序| 继承的例子

kotlin实现继承遗产 (Inheritance) Inheritance is a mechanism wherein a new class is derived from an existing class. 继承是一种机制&#xff0c;其中新类是从现有类派生的。 All Kotlin Classes have a common Superclass Any, it is the Default Superclass with no S…

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

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

nginx资源定向 css js路径问题

今天玩玩项目&#xff0c;学学nginx发现还不错&#xff0c;速度还可以&#xff0c;但是CSS JS确无法使用&#xff0c;原来Iginx配置时需要对不同类型的文件配置规则&#xff0c;真是很郁闷&#xff0c;不过想想也还是很有道理。闲暇之际&#xff0c;把配置贴上来。#user nobody…

五、库存查询功能的完善

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