C++ 虚函数和多态性

虚函数和多态性

派生类中与基类重名的成员

如果派生类的成员与基类的成员重名,则派生类的成员将隐藏同名的基类成员。

/*正方形类,基类*/
class Square
{
protected:double L;
public:Square() :L{ 4 } {};Square(double a) :L{ a } {}double GetArea() const  /*矩形面积*/{return L * L;}double GetGirth() const /*矩形周长*/{return  4 * L;}
};
/*矩形类,派生类*/
class Rectangle : public Square
{
protected:double W = 0;
public:Rectangle() = default;Rectangle(double a, double b) : Square{ a }, W{ b } {}double GetArea() const /*矩形面积*/{//重定义从基类继承的成员函数return L * W;}double GetGirth() const /*矩形周长*/{//重定义从基类继承的成员函数return  2 * (L + W);}
};int main(void)
{Rectangle Re{ 7,3 };cout << Re.GetArea() << endl; //调用派生类自定义的GetArea()成员函数cout << Re.GetGirth() << endl;//调用派生类自定义的GetGirth()成员函数return 0;
}

我们可以通过作用域运算符来使用一个被隐藏的基类成员。

	double GetArea() const /*矩形面积*/{if (L == W)return Square::GetArea(); //调用基类版本的GetArea()函数return L * W;}

虚函数

如果基类希望其派生类覆盖从基类继承的函数,则基类应当将该函数声明为虚函数。

  • 任何构造函数之外的非静态函数都可以是虚函数。
  • 关键字 virtual 只能出现在类内部的声明语句之前而不能用于类外部的函数定义。
  • 如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。
/*正方形类,基类*/
class Square
{
protected:double L;
public:Square() :L{ 4 } {};Square(double a) :L{ a } {}virtual double GetArea() const /*矩形面积*/{//虚函数return L * L;}virtual  double GetGirth() const /*矩形周长*/{//虚函数return  4 * L;}
};

C++11新标准允许派生类显式地注明它使用某个成员函数覆盖了它继承的虚函数。
具体做法是在形参列表后面、或者在 const 成员函数的 const 关键字后面、或者在引用成员函数的引用限定符后面添加一个关键字 override。

  • 在派生类中重新定义的虚函数,其函数名,函数类型,函数参数个数和类型必须与基类的虚函数相同。
  • 如果虚函数使用默认实参,则基类和派生类中定义的默认实参最好一致。
/*矩形类*/
class Rectangle : public Square
{
protected:double W = 0;
public:Rectangle() = default;Rectangle(double a, double b) : Square{ a }, W{ b } {}double GetArea() const override  /*矩形面积*/{return L * W;}double GetGirth() const override /*矩形周长*/{return  2 * (L + W);}
};

可以通过基类的指针或引用来访问派生类中与基类同名的成员函数。

int main(void)
{Cube cube{2};cout << cube.GetVolume()<< endl;Rectangle Re{ 3,3 };cout << "矩形面积:" << Re.GetArea() << endl;cout << "矩形周长:" << Re.GetGirth() << endl;Square * S = &Re;cout << S->GetArea() << endl; //调用派生类的GetArea()版本cout << S->GetGirth() << endl;//调用派生类的GetGirth()版本return 0;
}

虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
需要说明的是:当一个类带有虚函数时,编译系统会为该类构造一个虚函数表,它是一个指针数组,存放每个虚函数的入口地址。

虚析构函数

最好把基类的析构函数声明为虚函数。
将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不同。
构造函数不能声明为虚函数。
这是因为在执行构造函数时类对象还未完全建立过程,也无法把函数与类对象绑定。

多态性

在C++中,多态性表现形式之一是:具有不同功能的函数可以用同一个函数名,这样就可以实现用一个函数名调用不同内容的函数。
从系统实现的角度来看,多态性分为两类:

  • 静态多态性(编译时的多态性):通过函数重载和运算符重载实现。
  • 动态多态性(运行时的多态性):通过虚函数实现。

静态关联与动态关联

确定调用的具体对象的过程称为关联(binding),即把一个函数名与一个类对象捆绑在一起,建立关联。
静态关联:在编译阶段将函数名与类名“绑定”。
动态关联:在运行阶段将函数名与类名“绑定”。

纯虚函数和抽象基类

纯虚函数

纯虚函数(pure virtual function)纯虚函数是在声明虚函数时被“初始化”为 0 的函数。
声明纯虚函数的一般形式:

virtual 函数类型 函数名(参数列表)= 0;

纯虚函数只有函数名字而不具备功能。
纯虚函数的作用是:在基类中为其派生类保留一个函数名字,以便派生类根据需要对它进行定义。

抽象基类

含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类(abstract base class)。
抽象基类负责定义接口,而后续的其他类可以覆盖该接口。
值得注意:不能定义一个抽象基类的对象。
一个简单的抽象基类

/*形状类*/
class Shape
{
public:virtual double GetArea() = 0;	/*求面积*/virtual double GetGirth() = 0;  /*求周长*/
};

派生类

/*圆形类*/
class Circular : public Shape
{
private:const static double PI;double R = 0;
public:Circular() = default;Circular(double r);double GetArea() const override;    /*圆面积*/double GetGirth() const override;   /*圆周长*/
};
const double Circular::PI = 3.14;Circular::Circular(double r) : R{ r } {}
/*圆面积*/
double Circular::GetArea() const
{double S = PI * R * R;return S;
}
/*圆周长*/
double Circular::GetGirth() const
{double L = 2 * PI * R;return L;
}

通过基类的指针或引用来访问虚函数。

int main(void)
{//Shape Re; 不能定义一个抽象基类的对象。Circular Cir{ 10 };Shape * S = &Cir;cout << "圆面积:" << S->GetArea() << endl;cout << "圆周长:" << S->GetGirth() << endl; return 0;
}

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

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

相关文章

C#密封类、偏类

C#密封类 在C#中&#xff0c;密封类&#xff08;Sealed Class&#xff09;是一种特殊的类&#xff0c;它阻止其他类继承它。你可以通过在类定义前面加上 sealed 关键字来创建一个密封类。 以下是一个密封类的例子&#xff1a; public sealed class MyClass {// Class member…

【Redis】zset常用命令集合间操作内部编码使用场景

文章目录 前置知识列表、集合、有序集合三者的异同点 普通命令ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZPOPMAXBZPOPMAXZPOPMINBZPOPMINZRANKZREVRANKZSCOREZREMZREMRANGEBYRANKZREMRANGEBYSCOREZINCRBY 集合之间的操作ZINTERSTOREZUNIONSTORE 命令小结内部编码测试内部编…

PyTorch 成功安装验证

一、确认PyTorch版本 安装PyTorch之后&#xff0c;可以运行以下代码来确认PyTorch的版本&#xff1a; import torchprint(torch.__version__)如果没有报错&#xff0c;同时输出了正确版本号&#xff0c;就说明PyTorch已经成功安装了。 二、测试PyTorch基础功能 为了确保PyT…

C/C++---------------LeetCode第LCR. 024.反转链表

反转链表 题目及要求双指针 题目及要求 双指针 思路&#xff1a;遍历链表&#xff0c;并在访问各节点时修改 next 引用指向&#xff0c;首先&#xff0c;检查链表是否为空或者只有一个节点&#xff0c;如果是的话直接返回原始的头节点&#xff0c;然后使用三个指针来迭代整个…

DHCP配置命令

排除IP地址或IP范围&#xff08;保留&#xff09;&#xff1a; Switch(config)#ip dhcp excluded-address 192.168.10.1 192.168.10.10 为地址池取名&#xff1a; Switch(config)#ip dhcp pool name 分配地址范围&#xff1a; Switch(dhcp-config)#network 192.168.10.0 2…

windows 安装 Oracle Database 19c

目录 什么是 Oracle 数据库 下载 Oracle 数据库 解压文件 运行安装程序 测试连接 什么是 Oracle 数据库 Oracle数据库是由美国Oracle Corporation&#xff08;甲骨文公司&#xff09;开发和提供的一种关系型数据库管理系统&#xff0c;它是一种强大的关系型数据库管理系统…

lambda表达式c++

介绍 可调用对象 对于一个表达式&#xff0c;如果可以对其使用调用运算符&#xff08;&#xff09;&#xff0c;则称它为可调用对象。如函数就是一个可调用对象&#xff0c;当我们定义了一个函数f(int)时&#xff0c;我们可以通过f(5)来调用它。 可调用对象有&#xff1a; …

c语言中*p1++和p1++有啥区别

在C语言中&#xff0c;*p1和p1是两个不同的表达式&#xff0c;有以下区别&#xff1a; *p1&#xff1a;这是一个后缀递增运算符的组合。首先&#xff0c;*p1会取出指针p1所指向的值&#xff0c;并且对p1进行递增操作。简而言之&#xff0c;这个表达式会先取出p1指向的值&#x…

43-设计问题-最小栈

原题链接&#xff1a; 198. 打家劫舍 题目描述&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&a…

基于springboot实现校园在线拍卖系统项目【项目源码】计算机毕业设计

基于springboot实现校园在线拍卖系统演示 Javar技术 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&…

Java多线程核心技术第一阶段-Java多线程基础 02

接上篇&#xff1a;Java多线程核心技术第一阶段-Java多线程基础 01 3.3 清除中断状态的使用场景 this.interrupted()方法具有清除状态标志值的功能&#xff0c;借用此特性可以实现一些效果。 【示例3.3.1】在MyThread4线程中向list1和list2存放数据&#xff0c;基于单一职责原…

asp.net mvc点餐系统餐厅管理系统

1. 主要功能 ① 管理员、收银员、厨师的登录 ② 管理员查看、添加、删除菜品类型 ③ 管理员查看、添加、删除菜品&#xff0c;对菜品信息进行简介和封面的修改 ④ 收银员浏览、搜索菜品&#xff0c;加入购物车后进行结算&#xff0c;生成订单 ⑤ 厨师查看待完成菜品信息…

大数据-之LibrA数据库系统告警处理(ALM-12054 证书文件失效)

告警解释 系统在每天二十三点检查当前系统中的证书文件是否失效&#xff08;即当前集群中的证书文件是否过期&#xff0c;或者尚未生效&#xff09;。如果证书文件失效&#xff0c;产生该告警。 当重新导入一个正常证书&#xff0c;并且状态不为失效状态&#xff0c;该告警恢…

如何编写高质量和可维护的C++代码?

编写高质量和可维护的C代码需要遵循一些编码准则和最佳实践。以下是一些重要的建议&#xff1a; 一致的命名规范&#xff1a;选择明确、一致的命名风格&#xff0c;例如驼峰命名法或下划线命名法&#xff0c;并在整个代码库中保持一致。 使用有意义的变量和函数名&#xff1a;…

开拓经验专栏:从十来天的晨型人体验开始

文章目录 拓新缘起契机实践心得 拓新 确定要新开一个板块&#xff0c;用来记录持续自我提升的经验和教训&#xff0c;着实遭遇了不少阻力。 首先&#xff0c;我的语文功底一向不行&#xff0c;当年高考前&#xff0c;语文分数在及格线上下跳动都是常事&#xff0c;现在却要通…

python命名空间字典

1 python命名空间字典 python命名空间是属性与对象的一一映射关系。 任何一个属性通过命名空间都能找到唯一一个与之对应的对象。 详细点击《python命名空间基础知识》。 python的命名空间由字典实现&#xff0c;属性为键&#xff0c;对象为值。通过属性找到对象。 1.1 di…

DAC实验(DAC 输出三角波实验)(DAC 输出正弦波实验)

DAC 输出三角波实验 本实验我们来学习使用如何让 DAC 输出三角波&#xff0c;DAC 初始化部分还是用 DAC 输出实验 的&#xff0c;所以做本实验的前提是先学习 DAC 输出实验。 使用 DAC 输出三角波&#xff0c;通过 KEY0/KEY1 两个按键&#xff0c;控制 DAC1 的通道 1 输出两种…

深度学习中的Linear层

在深度学习中&#xff0c;Linear层被广泛应用于各种神经网络中。它是一种线性变换&#xff0c;将输入向量映射到输出向量。本文将介绍Linear层的基本功能&#xff0c;以及在深度学习中的应用。 一、Linear层的功能 Linear层&#xff0c;也被称为全连接层&#xff0c;是深度学…

LinkWeChat V4.9.8 版本发布

LinkWeChat v4.9.8 已经发布&#xff0c;基于企业微信的 SCRM 系统 LinkWeChat 是国内首个基于企业微信的开源 SCRM&#xff0c;在集成了企微强大的开放能力的基础上&#xff0c;进一步升级拓展灵活高效的客户运营能力及多元化精准营销能力&#xff0c;让客户与企业之间建立强…

Nginx反向代理和负载均衡

1.反向代理 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受internet上的连接请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;并将从服务器上得到的结果返回给internet上请求连接的客户端&#xff0c;此时代理服务器对外就表现为一…