零散记录,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);
相关推荐
假如让我当三天老蒯10 小时前
前端跨域解决方案(学习用)
前端·javascript·面试
Colin草率地做慢慢地改10 小时前
关于QuickStore这个项目的重构(2)- 数据库建表文件
后端·面试·架构
JieE21220 小时前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
JustHappy1 天前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom1 天前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
假如让我当三天老蒯1 天前
模块化:ES Module 与 CommonJS 的区别
前端·面试
沉默王二1 天前
面试官:RAG 不用向量数据库,用 MySQL 硬扛?我:100 万向量不是很轻松?
mysql·面试·ai编程
Darling噜啦啦2 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
swipe2 天前
正则表达式入门到进阶:从表单校验到手写模板引擎
前端·javascript·面试