零散记录,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);
相关推荐
xsgbbx3 小时前
我在 Windows 上把 DeepSeek-TUI 从安装跑到了代码生成
面试
搬砖码4 小时前
同源多标签页通信 4 种方案,从入门到生产环境
前端·面试
野生技术架构师4 小时前
我总结了这份2026最新版Java面试题库(背完这一套就够了)
java·开发语言·面试
张元清4 小时前
SSR 状态管理陷阱:defineStore vs defineContextStore
前端·javascript·面试
神三元5 小时前
最近半年,我做了个 AI-Native 的 Agent 从零到进阶教程
前端·javascript·面试
逻辑驱动的ken6 小时前
Java高频面试考点场景题23
java·开发语言·数据库·面试·职场和发展·哈希算法
沪漂阿龙6 小时前
大模型微调终极指南:从显存优化到高效推理,一文吃透所有核心技术
人工智能·面试
WJJAGI6 小时前
hermes+pentagi进行渗透测试
人工智能·面试
_风中无我。8 小时前
深圳行,面试笔记!
笔记·面试·职场和发展