详细介绍:ESP32 (基于 ESP-IDF) 应用开发相关 C++ 知识体系


想要高效地开发 ESP32 (基于 ESP-IDF) 的应用,掌握一定的 C++ 知识体系 能让你的代码更高效、可维护性更强。

C++ 基础语法

基础类型与语法

  • 变量与常量 (int, float, bool, const)
类型说明示例
int整型int a = 10;
float单精度浮点型float pi = 3.14f;
double双精度浮点型double e = 2.71828;
bool布尔型bool flag = true;
char字符型char c = 'A';
const常量修饰符const int MAX = 100;
  • 作用域与生命周期(局部变量 / 全局变量 / static)
类型作用域生命周期示例
局部变量函数内有效函数结束即销毁int x = 10;
全局变量整个文件可见程序运行期间int g = 5;
static 变量仅函数内可见,但值持续存在整个程序运行期间static int counter = 0;
  • 控制语句(if, switch, for, while)

if / else

int score = 85;
if (score >= 90) {
cout << "Excellent" << endl;
} else if (score >= 60) {
cout << "Pass" << endl;
} else {
cout << "Fail" << endl;
}

switch

int day = 3;
switch (day) {
case 1: cout << "Monday"; break;
case 2: cout << "Tuesday"; break;
case 3: cout << "Wednesday"; break;
default: cout << "Invalid day"; break;
}

循环语句

// for 循环
for (int i = 0; i < 5; i++) {
cout << i << " ";
}
// while 循环
int n = 0;
while (n < 3) {
cout << n << " ";
n++;
}
// do-while 循环
int m = 0;
do {
cout << m << " ";
m++;
} while (m < 2);
  • 函数定义与声明(参数传递、返回值、函数重载)

参数传递方式

方式示例特点
值传递int add(int x)传入副本,不影响原变量
引用传递int add(int &x)操作原变量(高效)
指针传递int add(int *x)通过地址访问(常用于数组)
// 函数声明(声明告诉编译器函数存在)
int add(int a, int b);
// 函数定义(实际实现)
int add(int a, int b) {
return a + b;
}

函数重载:C++ 允许多个同名函数,只要参数不同。

int sum(int a, int b) { return a + b; }
double sum(double a, double b) { return a + b; } // 重载

完整示例

#include <iostream>
  using namespace std;
  // 全局变量
  int globalCount = 0;
  // 函数声明
  int add(int a, int b);
  void showStaticCounter();
  // 主函数入口
  int main() {
  // 局部变量
  int x = 10;
  float y = 3.14;
  const int MAX = 100; // 常量
  cout << "变量示例: x=" << x << ", y=" << y << ", MAX=" << MAX << endl;
  // 控制语句
  if (x > 5) {
  cout << "x 大于 5" << endl;
  }
  // switch 示例
  int option = 2;
  switch (option) {
  case 1: cout << "选项1" << endl; break;
  case 2: cout << "选项2" << endl; break;
  default: cout << "未知选项" << endl; break;
  }
  // 循环语句
  cout << "for 循环输出: ";
  for (int i = 0; i < 3; i++) {
  cout << i << " ";
  }
  cout << endl;
  // 调用函数
  int result = add(5, 7);
  cout << "add(5,7) = " << result << endl;
  // static 示例
  showStaticCounter();
  showStaticCounter();
  showStaticCounter();
  return 0;
  }
  // 函数定义:两个数相加
  int add(int a, int b) {
  globalCount++; // 修改全局变量
  return a + b;
  }
  // static 变量演示:在函数内部保持状态
  void showStaticCounter() {
  static int counter = 0; // 静态局部变量
  counter++;
  cout << "static counter 调用次数: " << counter << endl;
  }

命名空间与文件组织

概念作用示例
namespace避免命名冲突Sensor::init()
using namespace std;简化代码(不推荐在库中使用)cout << "Hello";
.h / .cpp 分离声明与实现隔离led_controller.h / led_controller.cpp
  • namespace 使用(避免命名冲突)
    • namespace 名称 { … } 定义命名空间;
    • :: 表示“作用域解析运算符”,用于访问命名空间内的内容。

命名空间用于避免命名冲突,将变量、函数、类等分组管理。

例如:两个模块中都有名为 init() 的函数,可通过命名空间区分。

#include <iostream>
  namespace Sensor {
  void init() {
  std::cout << "Sensor initialized." << std::endl;
  }
  }
  namespace Network {
  void init() {
  std::cout << "Network initialized." << std::endl;
  }
  }
  int main() {
  Sensor::init();   // 调用传感器初始化
  Network::init();  // 调用网络初始化
  return 0;
  }
  • using namespace std; 的作用与风险

using namespace std; 允许直接使用 std 中的类和函数,无需写前缀

#include <iostream>
  using namespace std;
  int main() {
  cout << "Hello ESP-IDF C++!" << endl; // 无需写 std::
  }

注:会引发命名冲突,特别是在大型项目或使用第三方库(如 ESP-IDF)时。

using std::cout;
using std::endl;

始终使用完整前缀

std::cout << "Hello" << std::endl;
  • 头文件与实现文件分离(.h / .cpp)

在大型 ESP-IDF 项目中,建议将声明与实现分离,便于模块化与编译管理。

led_controller.h

#ifndef LED_CONTROLLER_H
#define LED_CONTROLLER_H
#include "driver/gpio.h"
namespace Device {
class LedController {
public:
explicit LedController(gpio_num_t pin);
void on();
void off();
void toggle();
private:
gpio_num_t _pin;
bool _state;
};
} // namespace Device
#endif // LED_CONTROLLER_H
  • led_controller.cpp
#include "led_controller.h"
namespace Device {
LedController::LedController(gpio_num_t pin)
: _pin(pin), _state(false) {
gpio_set_direction(_pin, GPIO_MODE_OUTPUT);
}
void LedController::on() {
_state = true;
gpio_set_level(_pin, 1);
}
void LedController::off() {
_state = false;
gpio_set_level(_pin, 0);
}
void LedController::toggle() {
_state = !_state;
gpio_set_level(_pin, _state);
}
} // namespace Device
  • main.cpp
#include <iostream>
  #include "led_controller.h"
  extern "C" void app_main() {
  Device::LedController led(GPIO_NUM_2);
  led.on();
  vTaskDelay(pdMS_TO_TICKS(1000));
  led.toggle();
  vTaskDelay(pdMS_TO_TICKS(1000));
  led.off();
  std::cout << "LED toggled using C++ class in ESP-IDF" << std::endl;
  }

指针与引用

  • 指针的定义与解引用
    • 指针(Pointer) 是保存 变量内存地址 的变量。
    • 使用 * 定义指针类型。
    • 使用 & 取变量的地址。
    • 使用 * 解引用(访问地址中的值)。
int a = 10;
int* p = &a;     // p 保存 a 的地址
cout << "a 的地址: " << &a << endl;
cout << "p 保存的地址: " << p << endl;
cout << "p 指向的值: " << *p << endl;  // 解引用指针
  • nullptr 与空指针判断
    • 空指针 是不指向任何有效对象的指针。
    • C++11 起推荐使用 nullptr(而不是旧的 NULL 或 0)。
int* p = nullptr; // 定义空指针
if (p == nullptr) {
cout << "p 是空指针" << endl;
}
int x = 42;
p = &x; // 让 p 指向有效地址
if (p != nullptr) {
cout << "p 指向的值为: " << *p << endl;
}
  • 引用 (&) 与引用传参
    • 引用(Reference) 是变量的别名。
    • 使用 & 定义引用,必须在定义时初始化。
    • 引用本身不是对象,没有独立地址。
    • 常用于函数参数,避免复制开销。
int a = 5;
int& ref = a;  // ref 是 a 的别名
ref = 10;      // 改变 ref 也会改变 a
cout << "a = " << a << endl;  // 输出 10
void addOne(int& num) {
num += 1;  // 修改原变量
}
int x = 10;
addOne(x);
cout << "x = " << x << endl;  // 输出 11
  • 动态内存管理 (new / delete)
    • 在堆(heap)上动态分配内存,用完后需手动释放。
    • new:分配内存
    • delete:释放内存
    • new[] / delete[]:用于数组
int* p = new int(5); // 动态分配单个整数
cout << "值为: " << *p << endl;
delete p; // 释放内存
int* arr = new int[3]{1, 2, 3}; // 动态数组
for (int i = 0; i < 3; i++) {
cout << arr[i] << " ";
}
delete[] arr; // 释放数组内存

注:不释放内存会导致「内存泄漏」。

  • 智能指针(std::unique_ptr / std::shared_ptr)

C++11 引入智能指针,用于自动管理内存,防止泄漏。

类型特点使用场景
std::unique_ptr独占所有权,不可拷贝独占资源场景
std::shared_ptr共享所有权,引用计数自动释放多个对象共享资源
#include <memory>
  #include <iostream>
    using namespace std;
    struct Dog {
    string name;
    Dog(string n) : name(n) { cout << name << " 出生 " << endl; }
    ~Dog() { cout << name << " 被销毁 " << endl; }
    };
    int main() {
    // unique_ptr 独占所有权
    unique_ptr<Dog> d1 = make_unique<Dog>("Lucky");
      // unique_ptr 不能拷贝,只能转移所有权
      unique_ptr<Dog> d2 = move(d1);
        if (!d1) cout << "d1 已转移所有权" << endl;
        // shared_ptr 共享所有权
        shared_ptr<Dog> s1 = make_shared<Dog>("Bobby");
          {
          shared_ptr<Dog> s2 = s1; // 引用计数 +1
            cout << "引用计数: " << s1.use_count() << endl;
            } // s2 离开作用域,计数 -1
            cout << "引用计数: " << s1.use_count() << endl;
            return 0;
            }

完整示例

#include <iostream>
  #include <memory>
    using namespace std;
    void addOneByRef(int& num) {
    num += 1; // 引用传参
    }
    void addOneByPtr(int* num) {
    if (num != nullptr)
    *num += 1; // 通过指针解引用修改
    }
    int main() {
    int a = 10;
    int* p = &a;
    cout << "指针解引用: *p = " << *p << endl;
    // 空指针判断
    int* np = nullptr;
    if (np == nullptr) cout << "np 是空指针" << endl;
    // 引用示例
    int& ref = a;
    ref = 20;
    cout << "引用修改后 a = " << a << endl;
    // 动态内存
    int* heapVar = new int(50);
    cout << "动态内存值: " << *heapVar << endl;
    delete heapVar; // 手动释放
    // 函数传参
    addOneByRef(a);
    cout << "引用传参后 a = " << a << endl;
    addOneByPtr(p);
    cout << "指针传参后 a = " << a << endl;
    // 智能指针
    unique_ptr<int> up = make_unique<int>(100);
      cout << "unique_ptr 值: " << *up << endl;
      shared_ptr<int> sp1 = make_shared<int>(200);
        shared_ptr<int> sp2 = sp1;
          cout << "shared_ptr 引用计数: " << sp1.use_count() << endl;
          return 0;
          }

栈(Stack)与堆(Heap)

特性栈 (Stack)堆 (Heap)
分配方式编译器自动分配 / 释放程序员手动分配 / 释放 (new / delete)
生命周期离开作用域自动销毁手动释放,否则内存泄漏
访问速度快(连续内存)慢(动态分配)
使用场景临时变量、函数参数动态数组、对象、跨函数传递数据
容量大小一般较小(几 KB ~ 几 MB)较大(系统可管理的全部 RAM)
  • 栈内存示例:栈上的变量由系统自动管理,不需要手动释放。
#include <iostream>
  using namespace std;
  void stackExample() {
  int a = 10;               // 栈上分配
  int b = 20;
  int sum = a + b;
  cout << "Stack Example: sum = " << sum << endl;
  } // 函数结束,a、b、sum 自动释放
  int main() {
  stackExample();
  cout << "Stack memory automatically released." << endl;
  return 0;
  }
  1. 所有局部变量 (a, b, sum) 都存放在 栈上。
  2. 当函数 stackExample() 结束,变量自动销毁,无需 delete。
  • 堆内存示例:堆内存由程序员动态分配,必须手动释放。
#include <iostream>
  using namespace std;
  void heapExample() {
  int* ptr = new int(42);  // 在堆上分配一个整数
  cout << "Heap value = " << *ptr << endl;
  delete ptr;              // 必须释放,否则内存泄漏
  ptr = nullptr;           // 防止悬空指针
  }
  int main() {
  heapExample();
  cout << "Heap memory manually released." << endl;
  return 0;
  }
  1. new int(42) 在堆上分配空间;
  2. delete ptr; 释放堆内存;
  3. 设置 ptr = nullptr 是良好习惯。
  • 数组对比示例(栈 vs 堆)
#include <iostream>
  using namespace std;
  int main() {
  // 栈上数组
  int stackArr[3] = {1, 2, 3};
  cout << "Stack array first element: " << stackArr[0] << endl;
  // 堆上数组
  int* heapArr = new int[3]{4, 5, 6};
  cout << "Heap array first element: " << heapArr[0] << endl;
  delete[] heapArr;  // 必须释放堆数组
  return 0;
  }
  1. 栈数组自动释放;
  2. 堆数组必须用 delete[] 释放。
  • 在 ESP-IDF 开发中的应用差异
场景推荐存储方式理由
临时计算变量简单、安全、速度快
FreeRTOS 任务栈空间每个任务有独立栈
长期存在的数据(如缓存)可动态分配较大内存
与 C 接口交互(如 malloc与 C 函数兼容
  • ESP-IDF 中堆内存函数
void* ptr = malloc(1024);  // 与 new 类似
free(ptr);
  • 智能指针(堆的安全封装):避免忘记释放 delete 导致的内存泄漏。
#include <iostream>
  #include <memory>
    using namespace std;
    void smartPtrExample() {
    unique_ptr<int> p = make_unique<int>(100); // 自动释放堆内存
      cout << "Smart pointer value = " << *p << endl;
      } // 作用域结束,自动调用 delete
项目栈 (Stack)堆 (Heap)
分配方式编译器自动程序员手动(new / delete
生命周期离开作用域自动销毁直到手动释放
性能相对慢
内存大小较小较大
常见问题栈溢出内存泄漏
管理方式自动手动或智能指针管理

面向对象编程 (OOP)

  • 类与对象的定义

    • 类(Class) 是对象的蓝图,定义对象的属性(成员变量)和行为(成员函数)。
    • 对象(Object) 是类的实例(实际存在的实体)。
  • 成员变量与成员函数

class Person {
public:
string name;
int age;
void sayHello() {
cout << "Hello, I'm " << name << ", age " << age << endl;
}
};
int main() {
Person p;          // 创建对象
p.name = "Tom";
p.age = 20;
p.sayHello();
return 0;
}
  • 访问修饰符:public / protected / private
修饰符访问范围使用场景
public类外可访问对外接口
protected派生类可访问,外部不可访问继承使用
private仅类内可访问内部实现
class Student {
private:
int id; // 私有成员,外部不能直接访问
public:
string name;
void setId(int newId) { id = newId; }
int getId() { return id; }
};
  • 构造函数、析构函数(包括默认构造、拷贝构造、移动构造)

    1. 构造函数(Constructor)
      • 在对象创建时自动调用。
      • 用于初始化成员变量。
      • 名称与类名相同,无返回值。
    2. 析构函数(Destructor)
      • 在对象销毁时自动调用。
      • 用于释放资源(如动态内存)。

默认构造函数

class Car {
public:
string brand;
int year;
// 默认构造函数
Car() {
brand = "Unknown";
year = 2000;
cout << "默认构造函数调用 " << endl;
}
void info() {
cout << brand << " - " << year << endl;
}
};

带参数构造函数

Car(string b, int y) {
brand = b;
year = y;
cout << "带参数构造函数调用 " << endl;
}

拷贝构造函数

Car(const Car& other) {
brand = other.brand;
year = other.year;
cout << "拷贝构造函数调用 " << endl;
}

移动构造函数(C++11)

Car(Car&& other) noexcept {
brand = move(other.brand);
year = other.year;
cout << "移动构造函数调用 ⚡️" << endl;
}
  • 拷贝构造函数: 拷贝构造函数用于创建一个新对象,作为现有对象的副本。它通常接受一个同类型对象的常量引用。

    • 它创建一个新对象,该对象是原对象的完整副本。
    • 原对象和新对象拥有独立的资源(深拷贝)。
    • 如果类管理着动态内存或其他资源,拷贝构造函数需要分配新内存并复制内容。
    • 拷贝构造函数可能会带来性能开销,特别是当对象很大或资源复制成本高时。
    • 拷贝构造: 创建独立副本,安全但可能有性能开销
  • 移动构造函数: 移动构造函数是C++11引入的新特性,它用于将资源从一个对象(通常是临时对象)转移到另一个对象,而无需复制。

    • 它接受一个同类型对象的右值引用(使用&&)。
    • 它“窃取”原对象的资源,而不是复制它们。
    • 移动后,原对象处于有效但未定义的状态(通常为空状态),因此不能再使用其资源。
    • 移动操作通常更高效,因为它避免了不必要的复制。
    • 移动构造: 转移资源所有权,高效但原对象会失效

析构函数

~Car() {
cout << "析构函数调用 " << endl;
}
  • this 指针
    • 每个成员函数内部都有一个隐藏的指针 this。
    • this 指向当前对象本身。
    • 常用于区分成员变量与参数同名的情况。
class Box {
private:
int width;
public:
void setWidth(int width) {
this->width = width; // 区分成员变量与参数
}
int getWidth() { return this->width; }
};
  • 类的初始化列表

初始化列表在构造函数执行前初始化成员变量,比在函数体内赋值更高效。

class Point {
private:
int x;
int y;
public:
Point(int a, int b) : x(a), y(b) { // 初始化列表
cout << "Point 初始化完成 (" << x << "," << y << ")" << endl;
}
};

完整示例

#include <iostream>
  #include <string>
    using namespace std;
    class Car {
    private:
    string brand;
    int year;
    public:
    // 默认构造函数
    Car() : brand("Unknown"), year(2000) {
    cout << "默认构造函数调用 " << endl;
    }
    // 带参数构造函数
    Car(string b, int y) : brand(b), year(y) {
    cout << "带参数构造函数调用 " << endl;
    }
    // 拷贝构造函数
    Car(const Car& other) {
    brand = other.brand;
    year = other.year;
    cout << "拷贝构造函数调用 " << endl;
    }
    // 移动构造函数
    Car(Car&& other) noexcept {
    brand = move(other.brand);
    year = other.year;
    cout << "移动构造函数调用 ⚡️" << endl;
    }
    // 析构函数
    ~Car() {
    cout << "析构函数调用  (" << brand << ")" << endl;
    }
    // 成员函数
    void showInfo() const {
    cout << "品牌: " << brand << ", 年份: " << year << endl;
    }
    // 使用 this 指针
    void setYear(int year) {
    this->year = year; // this 指向当前对象
    }
    };
    int main() {
    cout << "=== 创建对象 ===" << endl;
    Car c1;               // 默认构造
    Car c2("Tesla", 2024); // 带参数构造
    c2.showInfo();
    cout << "=== 拷贝构造 ===" << endl;
    Car c3 = c2; // 拷贝构造
    c3.showInfo();
    cout << "=== 移动构造 ===" << endl;
    Car c4 = move(c2); // 移动构造
    c4.showInfo();
    cout << "=== 修改属性 ===" << endl;
    c1.setYear(2025);
    c1.showInfo();
    cout << "=== 程序结束,析构函数自动调用 ===" << endl;
    return 0;
    }

C++ 进阶特性

面向对象高级特性

  • 继承 (class A : public B)

继承允许一个类获取另一个类的属性和方法。

class Derived : public Base { ... };

访问修饰符 public/protected/private 决定基类成员在派生类中的访问权限。

class Animal {
public:
void eat() { cout << "Animal eats" << endl; }
};
class Dog : public Animal { // 公有继承
public:
void bark() { cout << "Dog barks" << endl; }
};
int main() {
Dog d;
d.eat();  // 继承自 Animal
d.bark();
return 0;
}
  • 多态 (virtual 函数、纯虚函数)
  • 抽象类与接口类

虚函数 (Virtual Function):允许通过 基类指针/引用 调用派生类方法,实现运行时多态。

class Animal {
public:
virtual void speak() { cout << "Animal speaks" << endl; }
};
class Dog : public Animal {
public:
void speak() override { cout << "Dog barks" << endl; }
};
int main() {
Animal* a = new Dog();
a->speak(); // 输出 Dog barks
delete a;
}

纯虚函数与抽象类:

  1. 纯虚函数:virtual void func() = 0;
  2. 包含纯虚函数的类称为 抽象类,不能实例化。
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};
class Circle : public Shape {
public:
void draw() override { cout << "Draw Circle" << endl; }
};
int main() {
Shape* s = new Circle();
s->draw();
delete s;
}
  • 函数重载、运算符重载

函数重载:同名函数,根据参数类型/个数不同自动区分。

class Math {
public:
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
};

运算符重载:可以为自定义类型定义运算符行为。

class Point {
public:
int x, y;
Point(int a, int b) : x(a), y(b) {}
Point operator+(const Point& p) {
return Point(x + p.x, y + p.y);
}
};
int main() {
Point p1(1,2), p2(3,4);
Point p3 = p1 + p2;
cout << p3.x << "," << p3.y << endl; // 输出 4,6
}
  • 静态成员与单例模式

静态成员:static 修饰的成员属于类本身,而不是对象。

class Counter {
public:
static int count;
Counter() { count++; }
};
int Counter::count = 0;
int main() {
Counter c1, c2;
cout << "Count = " << Counter::count << endl; // 输出 2
}

单例模式(保证全局唯一实例)

class Singleton {
private:
static Singleton* instance;
Singleton() {} // 构造私有化
public:
static Singleton* getInstance() {
if (!instance) instance = new Singleton();
return instance;
}
void show() { cout << "Singleton instance" << endl; }
};
Singleton* Singleton::instance = nullptr;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
s1->show();
cout << "s1 == s2? " << (s1 == s2) << endl; // 输出 1 (true)
}

完整示例

#include <iostream>
  using namespace std;
  // 抽象基类
  class Animal {
  public:
  virtual void speak() = 0; // 纯虚函数
  };
  // 派生类
  class Dog : public Animal {
  public:
  void speak() override { cout << "Dog barks" << endl; }
  };
  // 运算符重载示例
  class Point {
  public:
  int x, y;
  Point(int a, int b) : x(a), y(b) {}
  Point operator+(const Point& p) { return Point(x + p.x, y + p.y); }
  };
  // 静态成员 & 单例模式
  class Counter {
  public:
  static int count;
  Counter() { count++; }
  };
  int Counter::count = 0;
  class Singleton {
  private:
  static Singleton* instance;
  Singleton() {}
  public:
  static Singleton* getInstance() {
  if (!instance) instance = new Singleton();
  return instance;
  }
  void show() { cout << "Singleton instance" << endl; }
  };
  Singleton* Singleton::instance = nullptr;
  int main() {
  // 多态示例
  Animal* a = new Dog();
  a->speak();
  delete a;
  // 运算符重载
  Point p1(1,2), p2(3,4);
  Point p3 = p1 + p2;
  cout << "Point p3: " << p3.x << "," << p3.y << endl;
  // 静态成员
  Counter c1, c2;
  cout << "Counter count: " << Counter::count << endl;
  // 单例模式
  Singleton* s1 = Singleton::getInstance();
  Singleton* s2 = Singleton::getInstance();
  s1->show();
  cout << "s1 == s2? " << (s1 == s2) << endl;
  return 0;
  }

模板编程

  • 函数模板 / 类模板

函数模板(Function Template):函数模板允许函数根据 传入类型自动生成 对应版本。

template<typename T>
  T add(T a, T b) {
  return a + b;
  }
#include <iostream>
  using namespace std;
  template<typename T>
    T add(T a, T b) {
    return a + b;
    }
    int main() {
    cout << add(1, 2) << endl;       // int 类型
    cout << add(3.5, 2.5) << endl;   // double 类型
    }

类模板(Class Template):类模板允许类在实例化时指定类型。

template<typename T>
  class Box {
  private:
  T value;
  public:
  Box(T v) : value(v) {}
  void show() { cout << "Value: " << value << endl; }
  };
  • 模板参数与类型推导

模板类型可以通过 调用时自动推导。

template<typename T>
  void print(T value) {
  cout << value << endl;
  }
  int main() {
  print(100);       // 自动推导 T=int
  print(3.14);      // 自动推导 T=double
  print("Hello");   // 自动推导 T=const char*
  }

可以指定多个模板参数

template<typename T1, typename T2>
  void show(T1 a, T2 b) {
  cout << a << ", " << b << endl;
  }
  • typename / template 关键字

typename:用于声明模板类型参数

template<typename T>  // 或 template<class T>
  class Example { ... };

typename 还可用于 依赖类型的嵌套定义

template<typename T>
  void func(typename T::value_type val) { ... }

template:在类模板内使用嵌套模板时,需要 template 关键字

template<class T>
  struct A {
  template<class U>
    void func(U u) { ... }
    };
  • 模板特化与偏特化(了解即可)

完全特化(Full Specialization):针对特定类型重新实现模板

template<>
  class Box<bool> { // bool 类型的特化
    public:
    void show() { cout << "This is a bool Box" << endl; }
    };

偏特化(Partial Specialization):对一类类型进行定制

template<typename T>
  class PtrBox<T*> { // 指针类型偏特化
    public:
    void show() { cout << "Pointer Box" << endl; }
    };

完整示例

#include <iostream>
  using namespace std;
  // 函数模板
  template<typename T>
    T add(T a, T b) {
    return a + b;
    }
    // 类模板
    template<typename T>
      class Box {
      private:
      T value;
      public:
      Box(T v) : value(v) {}
      void show() { cout << "Value: " << value << endl; }
      };
      // 类模板特化(bool)
      template<>
        class Box<bool> {
          public:
          void show() { cout << "This is a bool Box" << endl; }
          };
          int main() {
          // 函数模板示例
          cout << add(10, 20) << endl;
          cout << add(3.5, 2.5) << endl;
          // 类模板示例
          Box<int> b1(100);
            Box<double> b2(3.14);
              Box<bool> b3;
                b1.show();
                b2.show();
                b3.show();
                // 多模板参数
                template<typename T1, typename T2>
                  auto multiply(T1 a, T2 b) { return a * b; }
                  cout << multiply(2, 5.5) << endl; // 11.0
                  return 0;
                  }

STL(标准模板库)

  • 容器类:std::vector, std::map, std::queue, std::string

std::vector:动态数组,大小可变。

#include <vector>
  #include <iostream>
    using namespace std;
    int main() {
    vector<int> v = {1, 2, 3};
      v.push_back(4); // 添加元素
      v.pop_back();   // 删除末尾元素
      for (int x : v) cout << x << " ";
      cout << endl;
      }

std::map:键值对容器(有序)。

#include <map>
  int main() {
  map<string, int> m;
    m["Alice"] = 20;
    m["Bob"] = 25;
    for (auto& [key, value] : m)
    cout << key << ": " << value << endl;
    }

std::queue:队列,FIFO 结构。

#include <queue>
  int main() {
  queue<int> q;
    q.push(1);
    q.push(2);
    cout << q.front() << endl; // 1
    q.pop();
    cout << q.front() << endl; // 2
    }

std::string:字符串类,支持动态操作。

#include <string>
  int main() {
  string s = "Hello";
  s += " World";
  cout << s << endl;
  }
  • 算法:std::sort, std::find, std::for_each

std::sort

#include <algorithm>
  #include <vector>
    int main() {
    vector<int> v = {3,1,4,2};
      sort(v.begin(), v.end()); // 升序排序
      for(int x : v) cout << x << " ";
      }

std::find

#include <algorithm>
  int main() {
  vector<int> v = {1,2,3,4};
    auto it = find(v.begin(), v.end(), 3);
    if(it != v.end()) cout << "Found: " << *it << endl;
    }

std::for_each

#include <algorithm>
  #include <vector>
    int main() {
    vector<int> v = {1,2,3};
      for_each(v.begin(), v.end(), [](int x){ cout << x*2 << " "; });
      }
  • 迭代器

STL 容器支持迭代器访问。

#include <vector>
  int main() {
  vector<int> v = {1,2,3};
    for(auto it = v.begin(); it != v.end(); ++it)
    cout << *it << " ";
    }
  • std::pair, std::tuple

std::pair

#include <utility>
  int main() {
  pair<int,string> p = {1,"Alice"};
    cout << p.first << ", " << p.second << endl;
    }

std::tuple

#include <tuple>
  int main() {
  tuple<int,string,double> t = {1,"Bob",3.14};
    cout << get<0>(t) << ", " << get<1>(t) << ", " << get<2>(t) << endl;
      }
  • std::optional, std::variant(C++17)

std::optional:表示可能存在或不存在的值。

#include <optional>
  int main() {
  optional<int> opt;   // 空
    opt = 100;           // 赋值
    if(opt) cout << *opt << endl;
    }

std::variant:类型安全的联合类型(类似 union)。

#include <variant>
  int main() {
  variant<int,string> v;
    v = 10;
    cout << get<int>(v) << endl;
      v = "Hello";
      cout << get<string>(v) << endl;
        }

完整示例

#include <iostream>
  #include <vector>
    #include <map>
      #include <queue>
        #include <string>
          #include <algorithm>
            #include <tuple>
              #include <optional>
                #include <variant>
                  using namespace std;
                  int main() {
                  // vector
                  vector<int> v = {3,1,4,2};
                    sort(v.begin(), v.end());
                    cout << "vector: ";
                    for(int x : v) cout << x << " ";
                    cout << endl;
                    // map
                    map<string,int> m = {{"Alice",20},{"Bob",25}};
                      for(auto& [k,v] : m) cout << k << ":" << v << " ";
                      cout << endl;
                      // queue
                      queue<int> q;
                        q.push(10); q.push(20);
                        cout << "queue front: " << q.front() << endl;
                        q.pop();
                        // pair & tuple
                        pair<int,string> p = {1,"Tom"};
                          tuple<int,string,double> t = {2,"Jerry",3.14};
                            cout << p.first << "," << p.second << endl;
                            cout << get<0>(t) << "," << get<1>(t) << "," << get<2>(t) << endl;
                              // optional & variant
                              optional<int> opt = 42;
                                if(opt) cout << "optional: " << *opt << endl;
                                variant<int,string> var = "Hello";
                                  cout << "variant: " << get<string>(var) << endl;
                                    return 0;
                                    }

异常与错误处理

方法特点使用场景
try/catch/throwC++ 原生异常,自动栈展开通用 C++ 应用
esp_err_t + 返回值明确错误码,不依赖异常ESP-IDF / 嵌入式项目
ESP_ERROR_CHECK调试便利,失败自动 abortESP-IDF 常用调试宏
  • ry / catch / throw
    • throw:抛出异常
    • try:包裹可能抛出异常的代码
    • catch:捕获异常并处理
#include <iostream>
  #include <stdexcept>
    using namespace std;
    int divide(int a, int b) {
    if (b == 0) throw runtime_error("除数不能为零!");
    return a / b;
    }
    int main() {
    try {
    int result = divide(10, 0); // 会抛出异常
    cout << result << endl;
    } catch (const runtime_error& e) {
    cout << "捕获异常: " << e.what() << endl;
    }
    return 0;
    }
  • SP-IDF 通常禁用异常(-fno-exceptions),需掌握 错误码返回法 (esp_err_t)

ESP32 / ESP-IDF 默认禁用 C++ 异常(使用 -fno-exceptions 编译),
所以 通常不使用 try/catch/throw,而采用 错误码返回法。

esp_err_t 类型:ESP-IDF API 常用 esp_err_t 返回错误码

#include "esp_err.h"
#include <iostream>
  using namespace std;
  esp_err_t doSomething(int value) {
  if (value < 0) return ESP_ERR_INVALID_ARG;
  return ESP_OK;
  }
  int main() {
  esp_err_t err = doSomething(-1);
  if (err != ESP_OK) {
  cout << "错误码: " << err << endl;
  }
  return 0;
  }
  • 误检查宏:ESP_ERROR_CHECK(x)

宏定义

#define ESP_ERROR_CHECK(x) do {            \
esp_err_t rc = (x);                    \
if (rc != ESP_OK) {                    \
printf("ESP_ERROR_CHECK failed: %d\n", rc); \
abort();                            \
}                                      \
} while(0)

用法示例

#include "esp_err.h"
#include <iostream>
  using namespace std;
  esp_err_t initHardware(bool fail) {
  if (fail) return ESP_ERR_INVALID_STATE;
  return ESP_OK;
  }
  int main() {
  cout << "初始化硬件..." << endl;
  ESP_ERROR_CHECK(initHardware(false)); // 成功
  cout << "初始化成功" << endl;
  // ESP_ERROR_CHECK(initHardware(true)); // 若失败,程序会 abort
  return 0;
  }

注:ESP_ERROR_CHECK 在失败时会 打印错误并终止程序,常用于调试阶段。

完整示例

#include "esp_err.h"
#include <iostream>
  using namespace std;
  // 模拟函数:可能出错
  esp_err_t divideEsp(int a, int b, int* result) {
  if (b == 0) return ESP_ERR_INVALID_ARG; // 错误码返回
  *result = a / b;
  return ESP_OK;
  }
  int main() {
  int a = 10, b = 0;
  int result = 0;
  #ifdef USE_CPP_EXCEPTION
  // C++ 异常处理示例
  try {
  if (b == 0) throw runtime_error("除数不能为零!");
  result = a / b;
  cout << "结果: " << result << endl;
  } catch (const runtime_error& e) {
  cout << "捕获异常: " << e.what() << endl;
  }
  #else
  // ESP-IDF 错误码处理示例
  esp_err_t err = divideEsp(a, b, &result);
  if (err != ESP_OK) {
  cout << "错误码: " << err << endl;
  } else {
  cout << "结果: " << result << endl;
  }
  // 使用 ESP_ERROR_CHECK 宏
  // ESP_ERROR_CHECK(divideEsp(a, b, &result)); // 若 b=0,会 abort
  #endif
  return 0;
  }

C++11/14/17 特性(ESP-IDF 支持)

特性描述ESP-IDF 使用建议
auto自动类型推导方便迭代器、回调
constexpr编译期常量用于数组大小、配置常量
Lambda匿名函数任务/回调、for_each
enum class类型安全枚举推荐替代传统 enum
std::thread/std::mutexC++ 原生线程ESP32 推荐 FreeRTOS 任务/信号量
移动语义 (T&&)高效资源转移大对象/数组避免复制浪费 RAM
  • auto 类型推导

编译器根据初始化值自动推导变量类型。

#include <iostream>
  #include <vector>
    using namespace std;
    int main() {
    auto x = 42;         // int
    auto y = 3.14;       // double
    auto s = "hello";    // const char*
    vector<int> v = {1,2,3};
      for(auto it = v.begin(); it != v.end(); ++it) { // 自动推导迭代器类型
      cout << *it << " ";
      }
      cout << endl;
      }
  • constexpr 常量表达式

在编译期计算的常量。

constexpr int square(int x) { return x*x; }
int main() {
constexpr int a = square(5); // 编译期计算
int arr[a];                  // 可作为数组长度
cout << "a = " << a << endl;
}
  • Lambda 表达式(在任务/回调中非常有用)

匿名函数,可用于回调、任务。

#include <iostream>
  #include <algorithm>
    #include <vector>
      using namespace std;
      int main() {
      vector<int> v = {1,2,3,4};
        // 简单 lambda
        for_each(v.begin(), v.end(), [](int x){ cout << x*2 << " "; });
        cout << endl;
        // 捕获外部变量
        int factor = 3;
        for_each(v.begin(), v.end(), [factor](int x){ cout << x*factor << " "; });
        cout << endl;
        }

ESP-IDF 使用场景:可作为任务函数或回调

auto task = []() {
printf("FreeRTOS task lambda\n");
};
  • enum class

enum class(强类型枚举):避免与其他枚举冲突,类型安全。

enum class Color { Red, Green, Blue };
Color c = Color::Red;
if(c == Color::Red) cout << "Red selected" << endl;
  • std::thread / std::mutex(或使用 freertos/FreeRTOS.h 任务替代)

C++11 原生多线程(在 ESP32 上可以使用,FreeRTOS 更常用)

#include <thread>
  #include <mutex>
    #include <iostream>
      using namespace std;
      mutex mtx;
      void threadFunc(int id) {
      mtx.lock();
      cout << "Thread " << id << " running" << endl;
      mtx.unlock();
      }
      int main() {
      thread t1(threadFunc, 1);
      thread t2(threadFunc, 2);
      t1.join();
      t2.join();
      }

ESP-IDF 通常使用 FreeRTOS

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void myTask(void* param) {
printf("FreeRTOS task running\n");
vTaskDelete(NULL);
}
void app_main() {
xTaskCreate(myTask, "task1", 2048, NULL, 5, NULL);
}
  • 移动语义与右值引用 (T&&)

用于高效资源转移,减少拷贝。

#include <iostream>
  #include <vector>
    using namespace std;
    class Buffer {
    public:
    vector<int> data;
      Buffer(size_t n) : data(n, 0) {}
      // 移动构造
      Buffer(Buffer&& other) noexcept : data(move(other.data)) {
      cout << "Move constructor called\n";
      }
      // 拷贝构造
      Buffer(const Buffer& other) : data(other.data) {
      cout << "Copy constructor called\n";
      }
      };
      int main() {
      Buffer b1(5);
      Buffer b2 = move(b1); // 调用移动构造
      }

ESP-IDF 使用场景:移动大数组、对象,减少 RAM 消耗。

综合示例(ESP-IDF + C++11/14/17)

#include <iostream>
  #include <vector>
    #include <algorithm>
      #include <thread>
        #include <mutex>
          #include <utility> // move
            #include "freertos/FreeRTOS.h"
            #include "freertos/task.h"
            using namespace std;
            mutex mtx;
            // Buffer 类演示移动语义
            class Buffer {
            public:
            vector<int> data;
              Buffer(size_t n) : data(n, 0) {}
              Buffer(Buffer&& other) noexcept : data(move(other.data)) { cout << "Move constructor\n"; }
              };
              // Lambda 任务示例
              auto taskLambda = []() {
              printf("FreeRTOS task running via lambda\n");
              vTaskDelete(NULL);
              };
              int main() {
              // auto 类型
              auto x = 100;
              cout << "x = " << x << endl;
              // constexpr
              constexpr int y = 5;
              int arr[y] = {0};
              cout << "constexpr array size = " << sizeof(arr)/sizeof(arr[0]) << endl;
              // Lambda
              vector<int> v = {1,2,3};
                for_each(v.begin(), v.end(), [](int n){ cout << n*n << " "; });
                cout << endl;
                // enum class
                enum class Color { Red, Green };
                Color c = Color::Red;
                if(c == Color::Red) cout << "Red selected" << endl;
                // 移动语义
                Buffer b1(10);
                Buffer b2 = move(b1);
                // C++ 线程 (ESP32 上建议用 FreeRTOS)
                thread t1([](){ mtx.lock(); cout << "Thread running" << endl; mtx.unlock(); });
                t1.join();
                // ESP-IDF FreeRTOS 任务
                xTaskCreate((TaskFunction_t)taskLambda, "task1", 2048, NULL, 5, NULL);
                return 0;
                }

ESP-IDF 开发相关 C++ 知识应用

FreeRTOS 与 C++ 结合

功能C++ 封装方式说明
任务类 + static 成员函数通过 arg 访问对象成员
Queue模板类send / receive 封装队列操作
Semaphore / Mutex类封装lock / unlock
EventGroup类封装setBits / waitBits
  • 类中创建 FreeRTOS 任务(xTaskCreatePinnedToCore)

    • FreeRTOS 任务函数必须是 static 或普通函数,因为 C 风格函数指针不支持非静态成员函数。

    • 在类中使用 静态成员函数作为任务入口,并通过 void* arg 传递对象指针访问成员变量。

  • 使用类静态方法作为任务入口(static void task(void* arg))

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <iostream>
  using namespace std;
  class MyTask {
  private:
  int counter;
  // 静态任务入口
  static void taskFunc(void* arg) {
  MyTask* self = static_cast<MyTask*>(arg); // 转回对象指针
    for(int i=0; i<5; ++i) {
    self->counter++;
    printf("Counter: %d\n", self->counter);
    vTaskDelay(pdMS_TO_TICKS(500));
    }
    vTaskDelete(NULL); // 任务结束
    }
    public:
    MyTask() : counter(0) {}
    // 创建任务
    void start() {
    xTaskCreatePinnedToCore(taskFunc, "MyTask", 2048, this, 5, NULL, 0);
    }
    };
    extern "C" void app_main() {
    MyTask t;
    t.start();
    }
  • 在任务中访问对象成员变量
static void taskFunc(void* arg) {
MyTask* self = static_cast<MyTask*>(arg);
  self->counter++; // 访问成员变量
  }
  • 使用 Queue, Semaphore, EventGroup 封装类管理资源

Queue 封装

#include "freertos/queue.h"
template<typename T>
  class QueueWrapper {
  private:
  QueueHandle_t q;
  public:
  QueueWrapper(int size) {
  q = xQueueCreate(size, sizeof(T));
  }
  bool send(const T& item, TickType_t timeout=portMAX_DELAY) {
  return xQueueSend(q, &item, timeout) == pdTRUE;
  }
  bool receive(T& item, TickType_t timeout=portMAX_DELAY) {
  return xQueueReceive(q, &item, timeout) == pdTRUE;
  }
  };

Semaphore 封装

#include "freertos/semphr.h"
class MutexWrapper {
private:
SemaphoreHandle_t mtx;
public:
MutexWrapper() { mtx = xSemaphoreCreateMutex(); }
void lock() { xSemaphoreTake(mtx, portMAX_DELAY); }
void unlock() { xSemaphoreGive(mtx); }
};

EventGroup 封装

#include "freertos/event_groups.h"
class EventGroupWrapper {
private:
EventGroupHandle_t eg;
public:
EventGroupWrapper() { eg = xEventGroupCreate(); }
void setBits(EventBits_t bits) { xEventGroupSetBits(eg, bits); }
EventBits_t waitBits(EventBits_t bits, bool clearOnExit=true) {
return xEventGroupWaitBits(eg, bits, clearOnExit, true, portMAX_DELAY);
}
};

综合示例

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include <iostream>
  using namespace std;
  class MyTask {
  private:
  int counter;
  // 静态任务入口
  static void taskFunc(void* arg) {
  MyTask* self = static_cast<MyTask*>(arg);
    for(int i=0; i<5; ++i) {
    self->counter++;
    printf("Counter: %d\n", self->counter);
    vTaskDelay(pdMS_TO_TICKS(500));
    }
    vTaskDelete(NULL);
    }
    public:
    MyTask() : counter(0) {}
    void start() {
    xTaskCreatePinnedToCore(taskFunc, "MyTask", 2048, this, 5, NULL, 0);
    }
    };
    // Queue 封装
    template<typename T>
      class QueueWrapper {
      private:
      QueueHandle_t q;
      public:
      QueueWrapper(int size) { q = xQueueCreate(size, sizeof(T)); }
      bool send(const T& item) { return xQueueSend(q, &item, portMAX_DELAY) == pdTRUE; }
      bool receive(T& item) { return xQueueReceive(q, &item, portMAX_DELAY) == pdTRUE; }
      };
      // Mutex 封装
      class MutexWrapper {
      private:
      SemaphoreHandle_t mtx;
      public:
      MutexWrapper() { mtx = xSemaphoreCreateMutex(); }
      void lock() { xSemaphoreTake(mtx, portMAX_DELAY); }
      void unlock() { xSemaphoreGive(mtx); }
      };
      // EventGroup 封装
      class EventGroupWrapper {
      private:
      EventGroupHandle_t eg;
      public:
      EventGroupWrapper() { eg = xEventGroupCreate(); }
      void setBits(EventBits_t bits) { xEventGroupSetBits(eg, bits); }
      EventBits_t waitBits(EventBits_t bits) { return xEventGroupWaitBits(eg, bits, true, true, portMAX_DELAY); }
      };
      extern "C" void app_main() {
      MyTask t;
      t.start();
      QueueWrapper<int> q(10);
        q.send(42);
        int value;
        q.receive(value);
        printf("Queue received: %d\n", value);
        MutexWrapper mtx;
        mtx.lock();
        printf("Mutex locked\n");
        mtx.unlock();
        EventGroupWrapper eg;
        eg.setBits(0x01);
        EventBits_t bits = eg.waitBits(0x01);
        printf("Event bits: 0x%x\n", bits);
        }

驱动与外设抽象封装

外设封装方式RAII单例模式优点
GPIO类封装✔ 构造初始化,析构释放简化操作、自动管理
I2C类封装✔ 驱动安装/删除安全、复用
UART类封装✔ 单例系统控制台唯一实例
SPI类封装✔ 初始化/释放管理总线资源
  • 把外设驱动(如 GPIO、I2C、UART、SPI)封装为 C++ 类

GPIO 驱动封装:使用 C++ 类封装 GPIO,RAII 管理资源(构造函数初始化,析构函数释放)。

#include "driver/gpio.h"
#include <iostream>
  using namespace std;
  class GPIO {
  private:
  gpio_num_t pin;
  public:
  GPIO(gpio_num_t p, gpio_mode_t mode) : pin(p) {
  gpio_reset_pin(pin);
  gpio_set_direction(pin, mode);
  cout << "GPIO " << pin << " initialized\n";
  }
  void setHigh() { gpio_set_level(pin, 1); }
  void setLow() { gpio_set_level(pin, 0); }
  int read() { return gpio_get_level(pin); }
  ~GPIO() {
  gpio_reset_pin(pin);
  cout << "GPIO " << pin << " deinitialized\n";
  }
  };
extern "C" void app_main() {
GPIO led(GPIO_NUM_2, GPIO_MODE_OUTPUT);
led.setHigh();
vTaskDelay(1000 / portTICK_PERIOD_MS);
led.setLow();
}

I2C 驱动封装:封装初始化、读写,RAII 管理。

#include "driver/i2c.h"
#include <iostream>
  using namespace std;
  class I2CDevice {
  private:
  i2c_port_t port;
  uint8_t addr;
  public:
  I2CDevice(i2c_port_t p, uint8_t a) : port(p), addr(a) {
  i2c_config_t conf = {};
  conf.mode = I2C_MODE_MASTER;
  conf.sda_io_num = GPIO_NUM_21;
  conf.scl_io_num = GPIO_NUM_22;
  conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
  conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
  conf.master.clk_speed = 100000;
  i2c_param_config(port, &conf);
  i2c_driver_install(port, conf.mode, 0, 0, 0);
  cout << "I2C device 0x" << hex << int(addr) << " initialized\n";
  }
  ~I2CDevice() { i2c_driver_delete(port); }
  void writeByte(uint8_t reg, uint8_t data) {
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  i2c_master_start(cmd);
  i2c_master_write_byte(cmd, (addr<<1)|I2C_MASTER_WRITE, true);
  i2c_master_write_byte(cmd, reg, true);
  i2c_master_write_byte(cmd, data, true);
  i2c_master_stop(cmd);
  i2c_master_cmd_begin(port, cmd, pdMS_TO_TICKS(1000));
  i2c_cmd_link_delete(cmd);
  }
  };
  • RAII 管理资源(构造打开 / 析构关闭)
  • Singleton 模式用于驱动单例(例如系统 UART 控制台)

UART 封装 + 单例模式

#include "driver/uart.h"
#include <iostream>
  using namespace std;
  class UARTConsole {
  private:
  uart_port_t port;
  UARTConsole() : port(UART_NUM_0) {
  uart_config_t config = {115200, 8, UART_STOP_BITS_1, UART_PARITY_DISABLE, UART_HW_FLOWCTRL_DISABLE};
  uart_param_config(port, &config);
  uart_driver_install(port, 1024, 0, 0, NULL, 0);
  cout << "UART console initialized\n";
  }
  public:
  static UARTConsole& getInstance() {
  static UARTConsole instance;
  return instance;
  }
  void write(const char* msg) {
  uart_write_bytes(port, msg, strlen(msg));
  }
  // 禁止拷贝
  UARTConsole(const UARTConsole&) = delete;
  void operator=(const UARTConsole&) = delete;
  };
extern "C" void app_main() {
UARTConsole::getInstance().write("Hello UART\n");
}

SPI 驱动封装(示例)

#include "driver/spi_master.h"
#include <iostream>
  using namespace std;
  class SPIBus {
  private:
  spi_device_handle_t handle;
  public:
  SPIBus(spi_host_device_t host, int cs_pin) {
  spi_bus_config_t buscfg = {};
  buscfg.mosi_io_num = GPIO_NUM_23;
  buscfg.miso_io_num = GPIO_NUM_19;
  buscfg.sclk_io_num = GPIO_NUM_18;
  spi_bus_initialize(host, &buscfg, 1);
  spi_device_interface_config_t devcfg = {};
  devcfg.clock_speed_hz = 1*1000*1000;
  devcfg.spics_io_num = cs_pin;
  devcfg.mode = 0;
  spi_bus_add_device(host, &devcfg, &handle);
  cout << "SPI device initialized\n";
  }
  ~SPIBus() {
  spi_bus_remove_device(handle);
  spi_bus_free(HSPI_HOST);
  }
  // TODO: read/write SPI data
  };

综合示例(GPIO + UART 单例 + I2C)

extern "C" void app_main() {
// GPIO 输出
GPIO led(GPIO_NUM_2, GPIO_MODE_OUTPUT);
led.setHigh();
vTaskDelay(pdMS_TO_TICKS(500));
led.setLow();
// UART 输出(单例)
UARTConsole::getInstance().write("Hello UART Console\n");
// I2C 设备写入
I2CDevice dev(I2C_NUM_0, 0x3C);
dev.writeByte(0x00, 0xFF);
}

组件化与模块化设计

特性说明优点
独立模块类每个功能单独封装高内聚,易维护
接口类(纯虚类)定义模块交互低耦合,便于替换或测试
静态库 / 组件导出编译成组件,应用只依赖头文件模块化,复用性强
应用层调用通过接口操作隐藏实现,便于扩展
  • 每个功能模块(如传感器、网络、存储)单独成类或组件
    • 高内聚:模块内部实现细节封装。
    • 低耦合:模块间通过接口通信。
    • 易测试与替换。
// Sensor.h
#pragma once
class Sensor {
public:
virtual void init() = 0;       // 初始化传感器
virtual float readValue() = 0; // 获取数据
virtual ~Sensor() {}
};
// TemperatureSensor.h
#include "Sensor.h"
#include <iostream>
  class TemperatureSensor : public Sensor {
  public:
  void init() override {
  std::cout << "Temp sensor init" << std::endl;
  }
  float readValue() override {
  return 25.0f; // 模拟温度
  }
  };
  • 使用接口类定义模块交互(便于替换和测试)
    • 通过 纯虚类 定义接口,模块内部实现隐藏。
    • 方便替换实现或做 Mock 测试。
class Storage {
public:
virtual void writeData(const char* data) = 0;
virtual std::string readData() = 0;
virtual ~Storage() {}
};
// SPIFlashStorage.cpp
#include "Storage.h"
#include <iostream>
  class SPIFlashStorage : public Storage {
  public:
  void writeData(const char* data) override {
  std::cout << "Write to SPI flash: " << data << std::endl;
  }
  std::string readData() override {
  return "SPI flash data";
  }
  };
void logSensorData(Sensor* sensor, Storage* storage) {
float value = sensor->readValue();
char buf[32];
sprintf(buf, "Value=%.2f", value);
storage->writeData(buf);
}
  • 静态库或组件中导出 C++ 类接口
    • 在 组件或静态库中导出接口,应用层只依赖接口头文件。

ESP-IDF 组件化设计示例

components/
    my_sensor/
        include/
            Sensor.h
            TemperatureSensor.h
        src/
            TemperatureSensor.cpp

CMakeLists.txt

idf_component_register(SRCS "TemperatureSensor.cpp"
                       INCLUDE_DIRS "include")

应用层使用

#include "TemperatureSensor.h"
#include "Storage.h"
extern "C" void app_main() {
TemperatureSensor tempSensor;
SPIFlashStorage storage;
tempSensor.init();
logSensorData(&tempSensor, &storage);
}

综合示例

#include <iostream>
  using namespace std;
  // 接口类
  class Sensor {
  public:
  virtual void init() = 0;
  virtual float readValue() = 0;
  virtual ~Sensor() {}
  };
  class Storage {
  public:
  virtual void writeData(const char* data) = 0;
  virtual string readData() = 0;
  virtual ~Storage() {}
  };
  // 模块实现
  class TemperatureSensor : public Sensor {
  public:
  void init() override { cout << "Temperature sensor init" << endl; }
  float readValue() override { return 26.5f; }
  };
  class SPIFlashStorage : public Storage {
  public:
  void writeData(const char* data) override { cout << "Write: " << data << endl; }
  string readData() override { return "SPI data"; }
  };
  // 模块交互
  void logSensorData(Sensor* sensor, Storage* storage) {
  float val = sensor->readValue();
  char buf[32];
  sprintf(buf, "Value=%.2f", val);
  storage->writeData(buf);
  }
  // 应用层
  extern "C" void app_main() {
  TemperatureSensor tempSensor;
  SPIFlashStorage storage;
  tempSensor.init();
  logSensorData(&tempSensor, &storage);
  }

与 C 代码混用

功能C++ 封装方式说明
调用 C 函数extern "C"防止名字改编,C++ 可调用
封装 IDF APIC++ 类 + 成员函数对 esp_timer、esp_wifi 等 API 封装对象化接口
错误处理esp_err_t + 检查 / 宏判断返回值是否 ESP_OK,调试方便
  • extern “C”:在 C++ 文件中调用 C 函数
    • C++ 编译器默认会进行名称改编(name mangling),导致 C 函数链接失败。
    • 使用 extern “C” 告诉编译器按 C 规则编译,方便 C++ 调用 C 函数。
// my_c_code.h (C 文件头)
#ifndef MY_C_CODE_H
#define MY_C_CODE_H
#ifdef __cplusplus
extern "C" {
#endif
void my_c_function(int val);
#ifdef __cplusplus
}
#endif
#endif
// my_c_code.c
#include "my_c_code.h"
#include <stdio.h>
  void my_c_function(int val) {
  printf("C function called with %d\n", val);
  }
// main.cpp
#include "my_c_code.h"
int main() {
my_c_function(42); // 调用 C 函数
return 0;
}
  • C++ 封装 IDF API(例如 esp_wifi_init()、esp_timer_start_once())

    • ESP-IDF 大部分 API 是 C 风格,例如 esp_wifi_init()、esp_timer_start_once()。
    • 可以在 C++ 类中封装,保持 对象化调用,同时处理 esp_err_t 返回值。
#include "esp_err.h"
#include "esp_timer.h"
#include <iostream>
  using namespace std;
  class TimerWrapper {
  private:
  esp_timer_handle_t timer;
  static void timerCallback(void* arg) {
  TimerWrapper* self = static_cast<TimerWrapper*>(arg);
    cout << "Timer triggered!" << endl;
    }
    public:
    TimerWrapper() {
    esp_timer_create_args_t args = {};
    args.callback = &TimerWrapper::timerCallback;
    args.arg = this;
    args.name = "my_timer";
    esp_err_t err = esp_timer_create(&args, &timer);
    if(err != ESP_OK) {
    cout << "Timer create failed, err=" << err << endl;
    }
    }
    void startOnce(uint64_t delay_us) {
    esp_err_t err = esp_timer_start_once(timer, delay_us);
    if(err != ESP_OK) {
    cout << "Timer start failed, err=" << err << endl;
    }
    }
    ~TimerWrapper() {
    esp_timer_delete(timer);
    }
    };
  • 处理 esp_err_t 返回值

ESP-IDF API 都返回 esp_err_t,值为 ESP_OK 表示成功。

常用方式

esp_err_t err = esp_wifi_init(&cfg);
if(err != ESP_OK) {
cout << "WiFi init failed, err=" << err << endl;
}

使用宏封装

#define CHECK_ERR(x) do { \
esp_err_t __err = (x); \
if(__err != ESP_OK) { \
printf("Error %s:%d code=%d\n", __FILE__, __LINE__, __err); \
} \
} while(0)
CHECK_ERR(esp_wifi_init(&cfg));

综合示例

#include <iostream>
  #include "esp_err.h"
  #include "esp_timer.h"
  using namespace std;
  // C 函数示例
  extern "C" void c_hello(int val) {
  printf("C function called with %d\n", val);
  }
  // C++ 封装类
  class TimerWrapper {
  private:
  esp_timer_handle_t timer;
  static void timerCallback(void* arg) {
  TimerWrapper* self = static_cast<TimerWrapper*>(arg);
    cout << "Timer triggered!" << endl;
    }
    public:
    TimerWrapper() {
    esp_timer_create_args_t args = {};
    args.callback = &TimerWrapper::timerCallback;
    args.arg = this;
    args.name = "cpp_timer";
    esp_err_t err = esp_timer_create(&args, &timer);
    if(err != ESP_OK) {
    cout << "Timer create failed, err=" << err << endl;
    }
    }
    void startOnce(uint64_t delay_us) {
    esp_err_t err = esp_timer_start_once(timer, delay_us);
    if(err != ESP_OK) {
    cout << "Timer start failed, err=" << err << endl;
    }
    }
    ~TimerWrapper() {
    esp_timer_delete(timer);
    }
    };
    // 错误检查宏
    #define CHECK_ERR(x) do { \
    esp_err_t __err = (x); \
    if(__err != ESP_OK) { \
    printf("Error %s:%d code=%d\n", __FILE__, __LINE__, __err); \
    } \
    } while(0)
    extern "C" void app_main() {
    c_hello(100); // 调用 C 函数
    TimerWrapper t;
    t.startOnce(1000000); // 1秒后触发
    // 示例 esp_err_t API 调用
    esp_err_t err = esp_timer_start_once(timer, 500000); // 假设 timer 已定义
    if(err != ESP_OK) cout << "Start failed: " << err << endl;
    // 或使用宏
    // CHECK_ERR(esp_timer_start_once(t.timer, 500000));
    }

异步与回调机制

功能实现方式说明
回调机制std::function / Lambda灵活注册事件回调
任务间通信Queue / EventGroup异步消息传递、事件通知
异步执行std::future / FreeRTOS 事件组C++ 或 FreeRTOS 异步任务执行
  • 注册事件回调(用 std::function / Lambda)
    • 使用 std::function 存储回调函数,可接收 Lambda 或普通函数。

    • 适用于异步事件通知(如传感器数据就绪、WiFi 事件)。

#include <iostream>
  #include <functional>
    using namespace std;
    class EventSource {
    private:
    function<void(int)> callback; // 回调函数
      public:
      void registerCallback(function<void(int)> cb) {
        callback = cb;
        }
        void triggerEvent(int value) {
        if(callback) callback(value); // 调用回调
        }
        };
        int main() {
        EventSource evt;
        // 注册 lambda 回调
        evt.registerCallback([](int v){ cout << "Event received: " << v << endl; });
        evt.triggerEvent(42); // 异步模拟触发事件
        }
  • 任务间通信(Queue、EventGroup)

FreeRTOS 提供队列和事件组用于任务间异步通信。

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include <iostream>
  using namespace std;
  QueueHandle_t q;
  EventGroupHandle_t eg;
  void producer(void* arg) {
  int data = 123;
  xQueueSend(q, &data, portMAX_DELAY);
  xEventGroupSetBits(eg, 0x01);
  vTaskDelete(NULL);
  }
  void consumer(void* arg) {
  int recvData;
  xQueueReceive(q, &recvData, portMAX_DELAY);
  EventBits_t bits = xEventGroupWaitBits(eg, 0x01, true, true, portMAX_DELAY);
  printf("Consumer received: %d, event bits=0x%x\n", recvData, bits);
  vTaskDelete(NULL);
  }
  extern "C" void app_main() {
  q = xQueueCreate(1, sizeof(int));
  eg = xEventGroupCreate();
  xTaskCreate(producer, "producer", 2048, NULL, 5, NULL);
  xTaskCreate(consumer, "consumer", 2048, NULL, 5, NULL);
  }
  • 使用 std::future 或 FreeRTOS 事件模拟异步执行
    • std::future + std::async:标准 C++ 异步任务(ESP32 编译需支持)。

    • FreeRTOS 可用事件组 +任务模拟异步执行。

#include <iostream>
  #include <future>
    using namespace std;
    // 异步计算函数
    int asyncAdd(int a, int b) {
    return a + b;
    }
    int main() {
    // std::future + std::async
    future<int> result = async(asyncAdd, 10, 20);
      cout << "Async result: " << result.get() << endl;
      }

ESP-IDF / FreeRTOS 异步示例(事件 +任务)

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include <stdio.h>
  EventGroupHandle_t eg;
  void asyncTask(void* arg) {
  printf("Async task running...\n");
  vTaskDelay(pdMS_TO_TICKS(500));
  xEventGroupSetBits(eg, 0x01);
  vTaskDelete(NULL);
  }
  extern "C" void app_main() {
  eg = xEventGroupCreate();
  xTaskCreate(asyncTask, "asyncTask", 2048, NULL, 5, NULL);
  // 等待异步任务完成
  EventBits_t bits = xEventGroupWaitBits(eg, 0x01, true, true, portMAX_DELAY);
  printf("Async task finished, event bits=0x%x\n", bits);
  }

综合示例(回调 + 异步 + 任务间通信)

#include <iostream>
  #include <functional>
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "freertos/queue.h"
    using namespace std;
    QueueHandle_t q;
    class Sensor {
    private:
    function<void(float)> callback;
      public:
      void registerCallback(function<void(float)> cb) { callback = cb; }
        void readValue() {
        float value = 25.0f; // 模拟传感器
        if(callback) callback(value); // 触发回调
        xQueueSend(q, &value, portMAX_DELAY); // 异步任务发送
        }
        };
        void consumerTask(void* arg) {
        float val;
        xQueueReceive(q, &val, portMAX_DELAY);
        printf("Consumer received: %.2f\n", val);
        vTaskDelete(NULL);
        }
        extern "C" void app_main() {
        q = xQueueCreate(1, sizeof(float));
        Sensor sensor;
        // 注册回调
        sensor.registerCallback([](float v){ printf("Callback: %.2f\n", v); });
        // 启动消费者任务
        xTaskCreate(consumerTask, "consumer", 2048, NULL, 5, NULL);
        // 触发传感器读取
        sensor.readValue();
        }

ESP-IDF 实践常见 C++ 场景

应用场景涉及知识点
创建一个传感器类(如 DHT11)类封装、构造函数、成员函数
异步任务执行FreeRTOS 任务、Lambda、静态任务函数
Wi-Fi 连接管理状态机设计、回调、单例模式
日志与调试输出esp_log.h + std::string 格式化
封装 SPI 驱动析构清理、异常/错误码处理
OTA 更新类模板、RAII、错误处理
NVS 存储封装类封装 IDF C API 为 C++ 类接口

推荐学习顺序(针对 ESP32 开发者)

  1. 基础语法与OOP(类、构造、析构、继承)
  2. FreeRTOS 与任务通信(在类中用)
  3. C++11 特性(auto, lambda, smart pointer)
  4. 封装 ESP-IDF C API 为 C++ 类
  5. 模板与 STL 容器
  6. 异步回调与任务调度(std::function / FreeRTOS)
posted @ 2026-01-22 20:43  gccbuaa  阅读(37)  评论(0)    收藏  举报