零散记录,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);
相关推荐
浪浪山_大橙子4 小时前
公司里的 AI,终于不只会聊天:我用 GPT‑6 把企业工作伙伴开源了
前端·后端·面试
无限码力4 小时前
华为OD面试手撕真题 【合并相邻且相等的元素】多语言题解
华为od·面试·华为od面试真题·华为od面试手撕真题·华为od面试算法真题
乌暮5 小时前
JVM 原理与实践:从运行时数据区到线上故障排查
java·开发语言·jvm·后端·学习·面试
时间的拾荒人6 小时前
Qt 界面布局与容器控件详解:从分组框到布局管理器
开发语言·qt·面试
ShineWinsu7 小时前
对于Redis:ZSet类型的解析
数据库·c++·redis·缓存·面试·有序集合·zset
keke.shengfengpolang8 小时前
2026年财务经营分析校招技术能力拆解:SQL取数、Power BI看板、AI辅助分析,面试到底考什么
面试
王中阳Go8 小时前
简历写「QPS 提升 3 倍」,面试官问「怎么压测的」,我卡在并发数怎么定
人工智能·后端·面试
浩腾数字多媒体10 小时前
如何选择性价比高的地面互动公司?
面试
YangYang9YangYan11 小时前
2027 秋招|大数据管理与应用 企业数字化岗位面试高频问题拆解与求职准备
面试·职场和发展
唠点键盘之外的19 小时前
15 微调 vs RAG:到底怎么选
人工智能·机器学习·面试·aigc