# Class Tutorial **Repository Path**: hitcrt2027-rmvision-edu/class-tutorial ## Basic Information - **Project Name**: Class Tutorial - **Description**: 视觉类作业提交仓库 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-07 - **Last Updated**: 2026-09-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 课后作业:用 C++ 类与多态扩展检测后端 这是视觉组 C++ 类课程的课后作业。项目是一个可以独立编译的 YOLO 风格最小工程, 不依赖主项目、OpenCV、OpenVINO 或 yaml-cpp。 你需要在现有 `YoloV8Detector` 和 `Yolo11Detector` 的基础上,新增一个传统视觉检测后端, 并通过编译实验理解抽象类、虚函数、`override` 与基类指针。 ## 学习目标 完成作业后,你应该能够解释并实际使用: - 类、对象、构造函数和 `private` 成员; - `public` 继承与 `is-a` 关系; - 纯虚函数、抽象类和 `override`; - `std::unique_ptr` 如何保存不同派生类对象; - 为什么同一条 `detect()` 调用可以进入不同实现。 ## 起始代码说明 | 文件 | 作用 | | --- | --- | | `include/detector.hpp` | 类、继承关系和接口声明 | | `src/detector.cpp` | 检测后端与工厂函数的实现 | | `src/main.cpp` | 命令行入口和检测结果输出 | | `CMakeLists.txt` | CMake 编译配置 | 起始代码已经实现两种后端: - `YoloV8Detector`:每帧最多返回一个 `armor`; - `Yolo11Detector`:每帧最多返回 `armor` 和 `robot`。 项目中的检测结果是根据 `frame_id` 生成的模拟数据,不会进行真实图像推理。 ## 先运行起始项目 ```bash cmake -S . -B build cmake --build build ./build/cpp_oop_yolo_example yolov8 ./build/cpp_oop_yolo_example yolo11 ``` 也可以直接使用编译器: ```bash g++ -std=c++17 -Wall -Wextra -Wpedantic \ -Iinclude src/detector.cpp src/main.cpp -o cpp_oop_yolo_example ./cpp_oop_yolo_example yolo11 ``` 确认两个已有后端都能运行后,再开始下面的作业。代码中已经用 `TODO(作业 n)` 标出了主要修改位置。 ## 作业 1:新增 `TraditionalDetector` 新增一个 `TraditionalDetector` 类,要求: 1. 使用 `public` 继承 `DetectorBase`; 2. 类声明带有 `final`; 3. 构造函数接收 `float threshold`,保存到 `private` 成员 `threshold_`; 4. 使用 `override` 重写 `detect(int frame_id)`; 5. 使用 `override` 重写 `name() const`,返回字符串 `"traditional"`。 `detect()` 使用模拟数据即可,推荐按下面的规则实现: ```cpp confidence = 0.60F + static_cast(frame_id % 5) * 0.07F; ``` - 当 `confidence < threshold_` 时返回空结果; - 否则返回一个标签为 `"traditional_armor"` 的 `Detection`。 不要复制修改 `DetectorBase` 的接口来适配新类,新后端应主动遵守已有接口。 ## 作业 2:把新后端接入工厂函数 在 `Detector::make_backend()` 中增加 `traditional` 分支,创建并返回 `TraditionalDetector` 对象。 完成后下面的命令必须可以运行: ```bash ./build/cpp_oop_yolo_example traditional ``` 思考:`backend_` 的类型始终是 `std::unique_ptr`,为什么它仍然会调用 `TraditionalDetector::detect()`? ## 作业 3:观察“抽象类不能实例化”的编译错误 这是一个临时实验,不要把错误状态作为最终提交结果。 1. 先确保作业 1、2 已经可以正常编译; 2. 临时删除或注释 `TraditionalDetector` 中某一个纯虚函数的重写声明, 例如 `name() const override`; 3. 重新编译,找到包含 `abstract class` 或 `pure virtual` 的错误信息; 4. 用自己的话记录:为什么此时 `TraditionalDetector` 不能被创建? 5. 恢复被删除的函数,让项目重新编译通过。 注意:如果只删除 `.cpp` 中的函数定义,通常得到的是链接错误,而不是“抽象类”编译错误。 本实验应修改类声明,使派生类确实没有完成纯虚函数重写。 ## 验收标准 最终提交需要满足以下要求: - `yolov8`、`yolo11`、`traditional` 三种后端都能运行; - `TraditionalDetector` 正确使用继承、封装、纯虚函数重写和 `override`; - 工厂函数可以根据字符串创建三种不同后端; - 未知后端仍会输出错误并返回非零状态; - 已恢复作业 3 中故意制造的错误,最终代码可以正常编译; - 使用 `-Wall -Wextra -Wpedantic` 编译时没有新增警告; - 简要记录作业 3 的观察结果与回答。 建议提交修改后的工程,并在 README 末尾追加“我的实验记录”,也可以单独提交一份 `REPORT.md`。 ## 自测命令 ```bash cmake --build build ./build/cpp_oop_yolo_example yolov8 ./build/cpp_oop_yolo_example yolo11 ./build/cpp_oop_yolo_example traditional ./build/cpp_oop_yolo_example unknown ``` 前三条命令应正常结束;最后一条命令应报告 `unknown backend`。 ## 提示 - 可以直接参考 `YoloV8Detector` 的类声明和实现结构,但不要修改它的行为; - `override` 写在派生类成员函数声明后面; - `name()` 后面的 `const` 也是函数签名的一部分,不能漏掉; - 在工厂函数中使用 `std::make_unique(threshold)`; - 完成每一项后立即编译,出现问题更容易定位。 ## 与26自瞄主项目的对应关系 | 本作业 | 主项目 | | --- | --- | | `DetectorBase` | `auto_aim::YOLOBase` | | `YoloV8Detector` | `auto_aim::YOLOV8` | | `Yolo11Detector` | `auto_aim::YOLO11` | | `Detector` | `auto_aim::YOLO` | | `backend_` | `yolo_` | | `make_backend()` | `YOLO::YOLO()` 中按 `yolo_name` 创建对象的分支 | 主项目中的 `yolo.cpp` 也是在构造阶段选择具体后端,之后通过统一接口进行检测。 这份作业把依赖和推理过程去掉,只保留类设计、对象创建和运行时多态。 ## 我的实验记录 完成后请填写: 1. 作业 3 的关键编译错误: 2. `TraditionalDetector` 不能实例化的原因: