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

于 C++ 的虚函数多态 和 模板方法模式 的结合

要理解 LaneChangePath 如何 “调用父类 Execute 却返回自己的 Process”,核心在于 C++ 的虚函数多态 和 模板方法模式 的结合 —— 父类 PathGeneration 定义了固定的执行流程(Execute),子类 LaneChangePath 只需重写流程中的 “可变步骤”(Process),即可在复用父类流程的同时,定制自己的核心逻辑。以下从 “类继承关系”“语法细节”“调用流程” 三方面拆解:

一、先理清类的继承关系

首先明确代码中 3 个类的层级(核心是 “父类定义框架,子类定制细节”):

类名继承关系核心作用
Task最顶层基类定义规划任务的抽象接口(如 Execute 方法),是所有规划任务的统一基类。
PathGeneration继承 Task作为 “路径生成类” 的中间基类,封装固定的执行流程Execute 方法),并将 “可变的核心逻辑” 交给虚函数 Process
LaneChangePath继承 PathGeneration具体的 “换道路径生成类”,不重写 Execute(复用父类流程),只重写 Process(实现换道路径的核心生成逻辑)。

二、关键语法:虚函数(virtual)与方法重写(override

这是实现 “父类 Execute 调用子类 Process” 的核心语法,先看父类和子类的关键代码设计:

1. 父类 PathGeneration 的 “模板方法” 设计

PathGeneration 作为中间基类,用 模板方法模式 定义了 “固定流程 + 可变步骤”:

  • 固定流程Execute 方法(非虚函数,不可被子类重写),负责流程的 “框架”(如前置检查、日志记录、调用 Process);
  • 可变步骤Process 方法(声明为 virtual 虚函数,允许子类重写),负责流程的 “核心逻辑”(如换道路径生成、直线路径生成)。

父类核心代码回顾:

cpp

运行

class PathGeneration : public Task {public:// 1. 重写父类 Task 的 Execute 方法(固定流程,非虚函数)apollo::common::Status Execute(Frame* frame, ReferenceLineInfo* reference_line_info) override {Task::Execute(frame, reference_line_info);  // 调用顶层父类 Task 的 Execute(做基础初始化)return Process(frame, reference_line_info);  // 调用虚函数 Process(核心逻辑交给子类)}protected:// 2. 声明虚函数 Process(可变步骤,子类必须重写自己的逻辑)virtual apollo::common::Status Process(Frame* frame, ReferenceLineInfo* reference_line_info) {return apollo::common::Status::OK();  // 父类默认实现(空逻辑,子类需覆盖)}
};
  • 关键语法点 1:Process 用 virtual 修饰 → 声明为虚函数,允许子类重写,且调用时会根据 “对象的实际类型” 动态绑定到对应的实现(多态);
  • 关键语法点 2:Execute 没有 virtual → 是非虚函数,子类无法重写,强制复用父类的流程框架(保证所有子类的执行流程一致)。
2. 子类 LaneChangePath 的 “重写” 设计

LaneChangePath 作为具体的 “换道路径生成类”,不重写 Execute(直接用父类的流程),只重写 Process(实现换道路径的核心逻辑),代码如下:

cpp

运行

class LaneChangePath : public PathGeneration {private:// 重写父类 PathGeneration 的虚函数 Process(用 override 关键字确保重写正确)apollo::common::Status Process(Frame* frame, ReferenceLineInfo* reference_line_info) override {// 这里是 LaneChangePath 自己的核心逻辑:// 1. 计算换道的起始点和目标点(基于当前车道和目标车道);// 2. 生成换道的平滑路径(如基于多项式插值,满足车辆最小转弯半径);// 3. 校验路径是否避障(与障碍物的距离是否满足安全阈值);// 4. 将生成的路径存入 reference_line_info;return apollo::common::Status::OK();  // 返回自己的执行结果}
};
  • 关键语法点:override 关键字 → 显式声明 “该方法是重写父类的虚函数”,编译器会检查父类是否存在对应的虚函数,避免因函数签名不一致导致 “重写失败”(如参数类型错误、返回值不匹配)。

三、调用流程:为什么父类 Execute 会返回子类 Process 的结果?

当 LaneChangePath 的对象调用 Execute 时,整个流程会通过 “多态” 自动绑定到子类的 Process,具体步骤如下:

步骤 1:调用 LaneChangePath::Execute

由于 LaneChangePath 没有重写 Execute,会直接调用父类 PathGeneration 的 Execute 方法:

cpp

运行

// 假设创建了 LaneChangePath 的对象
std::unique_ptr<PathGeneration> path_task = std::make_unique<LaneChangePath>();
// 调用 Execute,实际执行的是父类 PathGeneration 的 Execute
path_task->Execute(frame, reference_line_info);
步骤 2:父类 Execute 内部调用 Process

进入父类 PathGeneration::Execute 后,代码会执行:

cpp

运行

return Process(frame, reference_line_info);  // 关键:调用虚函数 Process

此时,由于 Process 是虚函数,且 path_task 的实际类型是 LaneChangePath(虽然指针类型是父类 PathGeneration),C++ 会通过 “动态绑定” 找到 LaneChangePath 重写的 Process 方法,而非父类的默认实现。

步骤 3:返回子类 Process 的结果

LaneChangePath::Process 执行完换道路径生成逻辑后,返回自己的 Status(成功 / 失败),这个结果会被父类 Execute 直接返回给调用者。

最终效果:调用的是父类的 Execute 流程,但实际执行的是子类的 Process 逻辑,返回的也是子类 Process 的结果

四、这种设计的好处(为什么要这么写?)

Apollo 用这种 “父类框架 + 子类细节” 的设计,核心是为了复用流程、统一接口、灵活扩展

  1. 复用固定流程:所有路径生成类(如 LaneChangePathStraightPathAvoidObstaclePath)的 Execute 流程一致(如前置初始化、日志记录),无需每个子类重复写一遍;
  2. 统一接口:所有路径生成类都通过 PathGeneration 基类指针调用,调用者无需关心具体是哪个子类(如换道路径还是直线路径),符合 “开闭原则”;
  3. 灵活扩展:新增路径生成逻辑时(如 “紧急避障路径”),只需新建子类继承 PathGeneration,重写 Process 即可,无需修改父类代码。

总结:核心语法与设计模式

核心语法 / 模式作用
虚函数(virtual允许子类重写父类方法,实现 “动态绑定”(调用时根据对象实际类型找对应实现);
方法重写(override显式声明重写,避免语法错误,增强代码可读性;
模板方法模式父类定义固定流程(Execute),子类定制核心逻辑(Process),实现 “流程复用 + 细节灵活”;

简单说:父类 PathGeneration 搭好了 “架子”(Execute),子类 LaneChangePath 只需要填自己的 “核心内容”(Process),就能通过父类的架子完成整个流程,同时返回自己的结果。

http://www.dtcms.com/a/479583.html

相关文章:

  • 赤峰建设厅官方网站搞一个网站花多少钱
  • 【文献阅读】Deep Clustering with Self-supervision usingPairwise Data Similarities
  • 【文件的读写操作】
  • SLAM算法历史沿革及其论文出处
  • 用织梦后台修改网站logowordpress网站回调域
  • 网站怎么做文件下载门户网站安全建设方案
  • 【高频RFID】RC522芯片介绍与HC32/STM32应用
  • 专业的网站搭建多少钱企业网站开发摘要
  • 从可插拔拦截器出发:自定义、注入 Spring Boot、到生效路径的完整实践(Demo 版)
  • 网站域名区别吗常州seo外包公司
  • [GO]Go语言泛型详解
  • 网站设计的汕头公司咨询公司名称
  • 成都网站建设木子网络网站按条件显示广告图片
  • MySQL----存储过程和存储函数
  • python - 第三天
  • 可信赖的网站建设案例wordpress垂直分页导航插件
  • led外贸网站制作建设网站中期要做什么
  • H3C NQA+track 实现 双链路主备切换
  • 域名网站排名如何免费自做企业网站
  • 深圳龙岗做网站wordpress 用户 权限
  • 做毕业设计免费网站建设游民星空是谁做的网站
  • 合肥高端网站建设设计公司哪家好下载手机商城app下载安装
  • 35岁,挺好,慢慢来,比较快
  • Python爬虫实战:获取同花顺技术选股数据并做分析
  • 四平做网站佳业网络wordpress电商平台搭建
  • 10、Python流程控制-条件判断
  • 广州祥云平台网站建设如何建一个微信公众号
  • 详解AVL树旋转操作实现
  • 宁波网站推广外包服务湖南网络公司网站建设
  • Appsflyer Web2App :两种方式全解析