当前位置: 首页 > article >正文

C++中 为什么要把基类指针指向子类对象?

为什么要把基类指针指向子类对象?
1)实现多态性
动态绑定行为:通过基类指针指向子类对象,可以利用 C++ 的多态机制。当基类中有虚函数,并且子类重写了这些虚函数时,通过基类指针调用虚函数,实际调用的是对象所属子类中重写后的函数。这使得程序能够根据对象的实际类型(子类类型)来动态地选择正确的行为。例如,在一个图形绘制系统中,有一个基类Shape,包含虚函数draw,以及子类Circle和Rectangle分别重写了draw函数。

class Shape 
{
public:
    virtual void draw() = 0;
};
class Circle : public Shape 
{
public:
    void draw() override 
    {
        std::cout << "Drawing a circle." << std::endl;
    }
};
class Rectangle : public Shape 
{
public:
    void draw() override 
    {
        std::cout << "Drawing a rectangle." << std::endl;
    }
};

可以使用基类指针来操作不同类型的图形对象,如
Shape* shapePtr;
shapePtr = new Circle();
shapePtr->draw();,
这里shapePtr指向Circle对象时,draw函数会调用Circle类中的draw实现;当shapePtr指向Rectangle对象(shapePtr = new Rectangle();)时,draw函数会调用Rectangle类中的draw实现。这种动态绑定允许在不修改调用代码的情况下,轻松地添加新的图形子类并实现其特定的绘制行为,提高了代码的可扩展性和灵活性。
二)统一管理不同子类对象
容器存储和操作:可以将不同子类的对象存储在一个容器中,只要这些子类都继承自同一个基类。例如,使用std::vector<BaseClass*>这样的容器来存储多个不同类型的子类对象指针。在游戏开发中,假设有一个基类GameCharacter,子类有Warrior、Mage等不同类型的游戏角色。

class GameCharacter 
{
public:
    virtual void attack() = 0;
};
class Warrior : public GameCharacter 
{
public:
    void attack() override 
    {
        std::cout << "Warrior attacks with a sword." << std::endl;
    }
};
class Mage : public GameCharacter 
{
public:
    void attack() override 
    {
        std::cout << "Mage attacks with magic." << std::endl;
    }
};

可以创建一个角色指针的向量
std::vector<GameCharacter*> characterList;
characterList.push_back(new Warrior());
characterList.push_back(new Mage());
,然后通过遍历这个容器,使用基类指针调用每个角色的attack函数
(for (auto character : characterList) { character->attack(); }),这样就可以用统一的方式来处理不同类型的游戏角色的行为,而不需要为每个子类单独编写处理代码,使得代码更加简洁和易于维护。
三)代码复用和模块性
利用基类功能:子类继承了基类的属性和方法,当基类指针指向子类对象时,可以访问和使用基类中定义的公共成员函数和成员变量(在权限允许的情况下)。这有助于代码的复用,因为基类中的功能可以被多个子类共享。例如,在一个车辆管理系统中,有一个基类Vehicle定义了通用的属性(如车辆编号)和方法(如启动和停止车辆),子类Car和Truck继承自Vehicle。

class Vehicle 
{
protected:
    int vehicleId;
public:
    Vehicle(int id) : vehicleId(id) {}
    void start() 
    {
        std::cout << "Vehicle is starting." << std::endl;
    }
    void stop() 
    {
        std::cout << "Vehicle is stopping." << std::endl;
    }
};
class Car : public Vehicle 
{
public:
    Car(int id) : Vehicle(id) {}
    void honk() 
    {
        std::cout << "Car is honking." << std::endl;
    }
};
class Truck : public Vehicle 
{
public:
    Truck(int id) : Vehicle(id) {}
    void loadCargo() {
        std::cout << "Truck is loading cargo." << std::endl;
    }
};

通过基类指针(如
Vehicle* vehiclePtr; vehiclePtr = new Car(1);
vehiclePtr->start();)
可以调用start和stop这样的基类方法,无论指针实际指向的是Car还是Truck对象,都能复用基类中定义的这些通用功能,同时每个子类还可以有自己特有的功能(如Car的honk和Truck的loadCargo),这种方式提高了代码的模块性,使得基类和子类的功能划分更加清晰。


http://www.kler.cn/a/502206.html

相关文章:

  • 东芝3525AC彩色复印机复印默认成黑白模式方法
  • Java语言的正则表达式
  • 《自动驾驶与机器人中的SLAM技术》ch8:基于 IESKF 的紧耦合 LIO 系统
  • 无需昂贵GPU:本地部署开源AI项目LocalAI在消费级硬件上运行大模型
  • Cython全教程2 多种定义方式
  • 基于单片机的智能花卉浇水系统的设计与实现
  • STM32 FreeRTOS的任务创建和删除
  • 2_CSS3 背景 --[CSS3 进阶之路]
  • vue集成导出 txt文本文档 和 excel文档 的方法
  • jsdom爬虫程序中eBay主页内容爬取的异步处理
  • 从epoll事件的视角探讨TCP:三次握手、四次挥手、应用层与传输层之间的联系
  • 【论文笔记】Sign Language Video Retrieval with Free-Form Textual Queries
  • IDEA的Git界面(ALT+9)log选项不显示问题小记
  • 基于Java的推箱子游戏设计与实现
  • 31_搭建Redis分片集群
  • React中Fiber树构建过程详解——react中render一个App组件(包含子组件)的流程详解
  • 深度学习中的常见初始化方法:原理、应用与比较
  • C语言结构体如何实现位段
  • async++源码阅读——task模块
  • 【HM-React】08. Layout模块
  • 树状数组与线段树简单讲解与习题
  • SQLite 语法快速入门
  • 为AI聊天工具添加一个知识系统 之32 三“中”全“会”:推理式的ISA(父类)和IOS(母本)以及生成式CMN (双亲委派)之1
  • 数据预测2025年AI面试市场增幅超500%!
  • 机器学习 - 常用的损失函数(0-1、平方)
  • AIDD-人工智能药物设计-3DSMILES-GPT:基于词元化语言模型的3D分子生成