零散记录,ros实际开发中需要考虑的点

一.接口的抽象以及封装

比如说对电机驱动进行抽象。在多场景下,如果要驱动很多电机多种电机:大疆电机、小米电机、步进电机、伺服电机...但希望不管是什么种类的电机,都要驱动,那就要统一接口。

c++ 复制代码
class MotorDriver {
public:
    // 工厂模式创建不同类型电机
    static std::shared_ptr<MotorDriver> create_motor(...);
    
    // 纯虚函数定义统一接口
    virtual void init_motor() = 0;
    virtual void motor_mit_cmd(float f_p, float f_v, float f_kp, float f_kd, float f_t) = 0;
    virtual float get_motor_pos() = 0;
    // ...
};

virtual:虚函数 → 允许子类重写

= 0 :纯虚函数 → 父类不实现,子类必须实现

拥有纯虚函数的类叫 抽象类不能直接创建对象

arduino 复制代码
#ifndef MOTOR_DRIVER_HPP 
#define MOTOR_DRIVER_HPP
#include <memory>
// 抽象基类:统一所有电机接口
class MotorDriver { 
   public: // 电机类型枚举 enum class MotorType { STEPPER, SERVO }; 
   // 工厂方法:创建对应电机
    static std::shared_ptr<MotorDriver> create_motor(MotorType type); //
//纯虚函数:统一接口 ==========
    virtual void init_motor() = 0; 
    virtual void motor_mit_cmd(float f_p, float f_v, float f_kp, float f_kd, float f_t) = 0;       virtual float get_motor_pos() = 0;
// 多态必须虚析构 
    virtual ~MotorDriver() = default; 
 }; 
// 步进电机子类
class StepperMotor : public MotorDriver 
      { public: void init_motor() override; 
         void motor_mit_cmd(float f_p, float f_v, float f_kp, float f_kd, float f_t) override;          float get_motor_pos() override; }; 
         // 伺服电机子类 
 class ServoMotor :  public MotorDriver {//继承
       public: void init_motor() override;//重写虚函数
       void motor_mit_cmd(float f_p, float f_v, float f_kp, float f_kd, float f_t) override;          float get_motor_pos() override; };
    #endif

记住 子类重写override,重新实现这个函数功能。父类不实现功能,只定义接口。

二. this指针的使用

this 指针是隐含在每个非静态成员函数中的指针,指向调用该函数的对象。 this指针的作用

//c++ 复制代码
health = this->health_;    // 访问成员变量
info = this->info_;        // 访问成员变量
ans = this->createThread(); // 调用成员函数

为什么需要this指针? 当局部变量名与成员变量名冲突时,this指针优先要类的成员变量。

arduino 复制代码
class ImgDecode {
    int width;  // 成员变量
    
    void setWidth(int width) {  // 参数width = 局部变量
        this->width = width;    // this->width = 成员变量
    }
};

三. 工厂模式下的静态成员函数

js 复制代码
class LidarFactory {
public:
    // 静态成员函数:不需要创建对象就能调用
    static std::unique_ptr<LidarInterface> createLidar(LidarType type);
    static std::unique_ptr<LidarInterface> createLidar(const std::string& type_str);
    static bool isSupported(LidarType type);
    static std::vector<std::string> getSupportedTypes();
};

调用静态成员函数

js 复制代码
// 不需要创建 LidarFactory 对象,直接类名::调用
auto lidar = LidarFactory::createLidar("ydlidar");

注意:在这里,使用 unique_ptr 是为了防止内存泄漏。原始指针需要手动 delete,如果中途函数返回或抛出异常,delete 不会执行,就会导致内存泄漏。而 unique_ptr 是智能指针,在析构时会自动释放内存,非常安全。另外, unique_ptr 是独占所有权,不需要引用计数,性能也很好。因为雷达对象只需要一个拥有者。 shared_ptr 的引用计数机制有额外的性能开销,而且在多线程环境下需要原子操作保证计数正确。使用 unique_ptr 更轻量,且语义更清晰------明确表示只有一个拥有者。

arduino 复制代码
class YdLidarNodeV2 {
private:
  std::unique_ptr<LidarInterface> lidar_;  // 成员变量

public:
  bool initialize() {
    // 使用工厂创建雷达(自动管理内存)
    lidar_ = LidarFactory::createLidar("ydlidar");
    
    if (!lidar_) return false;
    
    // 调用多态方法
    lidar_->initialize(config);
    return true;
  }
  
  ~YdLidarNodeV2() {
    // 不需要手动 delete!
    // unique_ptr 析构时自动释放内存
  }
};

四.指针解引用

  • 看代码时注意 :遇到 -> 和 * 就想"这是解引用,在访问指针指向的内容"
  • 记住口诀 :
  • ptr = 指针(地址)
  • *ptr = 指针指向的内容(解引用)
  • ptr->member = 访问结构体成员(等价于 (*ptr).member
  • 指针是存储地址的变量,解引用就是通过这个地址访问真正的数据。在项目中, get_data(FrameBuf *frame_buf) 函数接收一个指针,然后用 frame_buf->start 来修改传入的FrameBuf变量,这样才能把相机数据传递出来。

1函数需要修改传入的变量

javascript 复制代码
// 如果不用指针
void get_data(FrameBuf frame_buf) {
    frame_buf.start = ...;  // 修改的是副本,外面的变量不会变!
}

// 用指针才能真正修改
void get_data(FrameBuf *frame_buf) {
    frame_buf->start = ...;  // 修改的是原变量
}

2传递大数据时省内存

arduino 复制代码
// 直接传结构体(会拷贝,慢且占内存)
void process_image(cv::Mat img);  // 拷贝整个图像

// 传指针(只传地址,快且省内存)
void process_image(cv::Mat *img);  // 只传4/8字节的地址

3动态分配内存

arduino 复制代码
int *arr = new int[100];  // 在堆上分配100个int的空间
delete[] arr;             // 用完要释放
c++ 复制代码
// 1. 创建变量
FrameBuf frame_buf;
// 2. 调用函数,传递地址
v4l2.get_data(&frame_buf);

// 3. 函数内部解引用,填充数据
int V4l2::get_data(FrameBuf *frame_buf) {
    // ... 获取数据 ...
    frame_buf->start = mmap_buffer[buf.index].start;  // 解引用写入
    frame_buf->length = buf.bytesused;
}
// 4. 回到主函数,使用数据
msg.data.assign(frame_buf.start, frame_buf.start + frame_buf.length);
相关推荐
kyriewen6 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
Revolution617 小时前
React 组件重新渲染时,到底重新执行了什么
前端·react.js·面试
Revolution619 小时前
第一次运行 Node.js:终端里的 JavaScript 怎样执行
后端·面试·node.js
进击的程序猿~9 小时前
Go 并发底层原理面试学习指南
开发语言·面试·golang
wdfk_prog10 小时前
嵌入式面试真题学习笔记系列
笔记·学习·面试
用户28735553259214 小时前
OpenResty 中 Lua 脚本实战示例(分场景讲清 Lua 解决了 Nginx 原生做不到的事)
面试
帅次14 小时前
Android 应用高级面试:Window 近1年高频追问 18 题
android·面试·职场和发展
Revolution6115 小时前
Node.js 是什么:前端项目里哪些事情由它完成
前端·面试·node.js
GitLqr1 天前
深入理解 Flutter 架构:从 Widget 到 GPU 像素的全链路解析
flutter·面试·dart
众人皆醒我独醉1 天前
TGI:HuggingFace 的官方推理服务——不止 PagedAttention,更懂模型生态
面试·llm·ai编程