C++学习笔记-基类、派生类与虚函数关系

news/2024/9/23 4:29:17/

在C++的面向对象编程中,基类(Base Class)、派生类(Derived Class)以及虚函数(Virtual Functions)构成了多态性的基石。这三者之间的关系错综复杂而又紧密相连,它们共同支撑起C++中复杂而灵活的类继承体系。

一、基类与派生类的基础

1.1 基类的定义与作用

基类(Base Class)是面向对象编程中继承的起点,它定义了派生类将继承的属性和行为。基类可以包含数据成员和成员函数,这些数据成员和成员函数可以被派生类继承和使用。基类的主要作用是为派生类提供一个共同的接口和一组基本的实现,使得派生类可以共享基类的代码和数据结构。

1.2 派生类的定义与继承方式

派生类(Derived Class)是从一个或多个基类继承而来的类。在C++中,派生类通过继承机制获得基类的成员(包括数据成员和成员函数),并且可以在此基础上添加新的成员或修改继承而来的成员。C++支持三种继承方式:公有继承(public inheritance)、保护继承(protected inheritance)和私有继承(private inheritance)。

  • 公有继承:基类的公有成员和保护成员在派生类中保持原有的访问级别(公有或保护),而基类的私有成员在派生类中仍然不可访问。
    保护继承:基类的公有成员和保护成员在派生类中都将变为保护成员,私有成员仍然不可访问。
  • 私有继承:基类的所有成员(公有成员、保护成员和私有成员)在派生类中都将变为私有成员,这意味着派生类外部的代码无法直接访问这些成员。

1.3 继承与多态性的关系

继承是面向对象编程中实现多态性的基础。多态性允许通过基类类型的指针或引用来调用派生类中的成员函数,而具体调用哪个函数则是在运行时根据对象的实际类型来确定的。这种机制使得程序更加灵活和可扩展。

二、虚函数与多态性

2.1 虚函数的定义与作用

虚函数是C++中实现多态性的关键机制之一。在基类中,使用virtual关键字声明的成员函数称为虚函数。虚函数允许在派生类中被重写(Override),即派生类可以提供一个与基类虚函数具有相同签名的函数,以替换基类中的实现。

虚函数的主要作用是实现多态性。通过基类指针或引用来调用虚函数时,将根据实际对象的类型来决定调用哪个版本的函数。这种机制使得我们可以在不知道具体对象类型的情况下,编写出能够处理多种类型的代码。

2.2 虚函数的实现机制

虚函数的实现依赖于C++的虚函数表(Virtual Table,简称vtable)和虚指针(Virtual Pointer,简称vptr)。每个包含虚函数的类都有一个虚表,虚表中存储了该类中所有虚函数的地址。当对象被创建时,编译器会在对象的内存布局中添加一个指向其虚表的指针(vptr)。通过这个指针,程序可以在运行时确定要调用的虚函数的具体地址。

2.3 虚析构函数

虚析构函数是一个特殊的虚函数,它用于在通过基类指针删除派生类对象时,确保能够调用到派生类的析构函数,从而正确释放派生类对象所占用的资源。如果基类的析构函数不是虚函数,那么在删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,这可能导致资源泄露或其他问题。

三、基类、派生类与虚函数的关系

3.1 继承与虚函数的关系

在C++中,虚函数通常定义在基类中,并在派生类中被重写。通过基类指针或引用来调用虚函数时,将根据实际对象的类型来调用相应的函数版本。这种机制使得我们可以在不知道具体对象类型的情况下,编写出能够处理多种类型的代码。

3.2 多态性的实现

多态性的实现依赖于基类中定义的虚函数和派生类中对这些虚函数的重写。当通过基类类型的指针或引用来调用虚函数时,程序会在运行时根据对象的实际类型来确定要调用的函数版本。这种机制使得我们可以编写出更加灵活和可扩展的代码。

3.3 虚函数与纯虚函数

在C++中,纯虚函数是一种特殊的虚函数,它在基类中只声明而不实现,并且在类声明结束时使用= 0来标识。包含至少一个纯虚函数的类被称为抽象类(Abstract Class),抽象类不能被实例化。纯虚函数的主要作用是为派生类提供一个必须实现的接口,从而确保派生类具有某种特定的行为。
###3. 4. 虚函数与抽象基类
虽然虚函数可以定义在基类中并在派生类中被重写,但基类本身并不需要是抽象的。然而,当基类中包含至少一个纯虚函数时,该基类就变成了抽象基类。纯虚函数是一种特殊的虚函数,它在基类中只声明而不实现(使用= 0来标识)。抽象基类不能被实例化,但它可以作为派生类的基类。通过定义纯虚函数,抽象基类为派生类提供了一个必须实现的接口,从而确保了派生类具有某种特定的行为。

四、程序示例

下面是一个简单的C++示例,它展示了基类、派生类和虚函数之间的关系。这个示例包括一个基类Animal,它定义了一个虚函数makeSound(),以及两个派生类Dog和Cat,它们分别重写了makeSound()函数。

#include <iostream>  
#include <string>  // 基类  
class Animal {  
public:  // 虚函数  virtual void makeSound() const {  std::cout << "Some generic animal sound" << std::endl;  }  // 虚析构函数(好习惯,尽管在这个简单示例中可能不是必需的)  virtual ~Animal() {}  
};  // 派生类 Dog  
class Dog : public Animal {  
public:  // 重写虚函数  void makeSound() const override {  std::cout << "Woof!" << std::endl;  }  
};  // 派生类 Cat  
class Cat : public Animal {  
public:  // 重写虚函数  void makeSound() const override {  std::cout << "Meow!" << std::endl;  }  
};  // 主函数,展示多态性  
int main() {  // 基类指针,指向派生类对象  Animal* myAnimal1 = new Dog();  Animal* myAnimal2 = new Cat();  // 通过基类指针调用虚函数,展示多态性  myAnimal1->makeSound(); // 输出: Woof!  myAnimal2->makeSound(); // 输出: Meow!  // 清理资源  delete myAnimal1;  delete myAnimal2;  return 0;  
}
  • 基类 Animal:

定义了一个虚函数 makeSound(),该函数在基类中有一个默认实现,输出一个通用的动物声音。
定义了一个虚析构函数,这是一个好习惯,因为它允许通过基类指针删除派生类对象时,能够调用到派生类的析构函数,从而正确释放资源。然而,在这个简单的示例中,由于我们没有在基类或派生类中分配任何动态内存,所以虚析构函数可能不是必需的。但在更复杂的场景中,它是非常重要的。

  • 派生类 Dog 和 Cat:

分别从基类 Animal 继承而来。
重写了基类中的虚函数 makeSound(),提供了各自的实现(狗叫“Woof!”和猫叫“Meow!”)。

  • 主函数 main:

创建了两个基类类型的指针 myAnimal1 和 myAnimal2,但它们分别指向 Dog 和 Cat 类型的对象。
通过这两个基类指针调用 makeSound() 函数时,由于 makeSound() 是虚函数,所以调用的是指针所指向对象的实际类型(即 Dog 或 Cat)中的函数版本,这展示了多态性。


http://www.ppmy.cn/news/1501327.html

相关文章

划分型dp,CF 1935C - Messenger in MAC

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1935C - Messenger in MAC 二、解题报告 1、思路分析 比较简单的思路是反悔贪心&#xff0c;这里不展开说了&#xff0c;来说一下dp的做法 由于式子里面带绝对值&#xff0c;很烦&#xff0c;我们将pair按…

计算机毕业设计hadoop+spark+hive知识图谱股票推荐系统 股票数据分析可视化大屏 股票基金爬虫 股票基金大数据 机器学习 大数据毕业设计

目 录 摘 要 Abstract 第1章 前 言 1.1 项目的背景和意义 1.2 研究现状 1.3 项目的目标和范围 1.4 论文结构简介 第2章 技术与原理 2.1 开发原理 2.2 开发工具 2.3 关键技术 第3章 需求建模 3.1 系统可行性分析 3.2 功能需求分析 3.3 非功能性…

算法:动态规划,贪心算法和NP完全性

动态规划 动态规划算法与分治法类似&#xff0c;其基本思想也是将待求解问题分解成若干个子问题&#xff0c;但是经分解得到的子问题往往不是互相独立的。不同子问题的数目常常只有多项式量级。在用分治法求解时&#xff0c;有些子问题被重复计算了许多次。如果能够保存已解决的…

c++网络编程实战——开发基于ftp协议的文件传输模块(三) 封装自己的ftp客户端

一.前言 在前面我们已经简单介绍过FTP协议的基本概念以及我们如何配置FTP服务和一些常用的ftp命令,这篇文章主要是介绍我们如何基于开源库去封装我们自己的ftp客户端。这篇文章也可以看做一篇介绍如何基于开源库去封装自己工具库的教程。 补充: 在上一篇文章中我犯了一个小错…

Vue3通用页面(表格,页头,分页,编辑,删除)

<script lang"ts" setup name"Canset-JDfund"> import { AddJDsetAPI, EditJDsetAPI, DelJDsetAPI, GetWLFsetListtAPI } from /api/canset/tfset // #region *****************XXXX模块**************** // #endregion const params {pageNo: 1, /…

Symfony 入门指南:快速安装与基础配置

Symfony 入门指南&#xff1a;快速安装与基础配置 Symfony 是一个强大而灵活的 PHP 框架&#xff0c;广泛应用于构建现代 Web 应用程序。本指南将带您一步一步地了解如何快速安装 Symfony&#xff0c;并完成基本配置&#xff0c;以便您能够开始使用这个强大的框架。 目录 引…

【java基础】spring springMVC springboot 的区别

Spring, Spring MVC, 和 Spring Boot 是三个紧密相关的技术&#xff0c;它们都是由 Pivotal 团队&#xff08;原SpringSource&#xff09;开发的&#xff0c;主要用于构建企业级的Java应用程序。尽管它们在功能上有所交集&#xff0c;但各自也有独特的定位和用途。 Spring Fra…

vue + xterm 前端终端terminal

引入 import {Terminal} from "xterm"; import {FitAddon} from "xterm-addon-fit"; import "xterm/css/xterm.css";html <div id"terminal"></div>vue onMounted(() > {nextTick(() > {initTerm();}) })const i…