C++入门(基于ESP-IDF)

主要参考资料:
B站Up主孤独的二进制《ESP32上的面向对象》
乐鑫官网C++ Support: https://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.3/esp32/api-guides/cplusplus.html
多态: https://blog.csdn.net/weixin_50569901/article/details/124003064

C++历史

  1. 起源(1979年):
    Bjarne Stroustrup在贝尔实验室开始工作时,为了增强C语言的功能,开始研究一种新的语言。
    在这里插入图片描述

  2. “C with Classes”(1980-1983年):
    Stroustrup将新特性加入C语言中,包括类(classes)和强类型功能,这个新语言最初被称为"C with Classes"。

  3. C++的命名(1983年):
    为了强调这种语言是C语言的扩展,并且支持操作符重载,使得它可以用来模拟多种不同的数据类型,Stroustrup将语言命名为C++。

  4. 首个公开发行版(1985年):
    C++的第一个公开发行版发布,这个版本包括了异常处理、函数名重载(函数重载)和引用等特性。

  5. ANSI C++标准(1998年):
    C++的第一个正式标准由国际标准化组织(ISO)发布,被称为ISO/IEC 14882:1998,通常称为C++98。

  6. C++11(2011年):
    这是C++语言的一次重大更新,引入了大量新特性,如自动类型推导(auto)、基于范围的for循环、智能指针、lambda表达式、右值引用和移动语义等。

  7. C++14(2014年):
    作为C++11的增量式改进,C++14引入了泛型lambda表达式、返回类型推导、二进制字面量等特性。

  8. C++17(2017年):
    这个版本继续改进语言,包括结构化绑定、if constexpr、std::optional、std::variant等特性。

  9. C++20(2020年):
    C++20是C++语言的另一个重要里程碑,它引入了概念(concepts)、协程(coroutines)、模块(modules)、范围(ranges)等特性。

在ESP-IDF开发中使用C++

默认情况下,ESP-IDF 使用 C++23 语言标准和 GNU 扩展 (-std=gnu++23) 编译 C++ 代码。

要使用其他语言标准编译特定组件的源代码,请按以下步骤,在组件的 CMakeLists.txt 文件中设置所需的编译器标志:

idf_component_register( ... )
target_compile_options(${COMPONENT_LIB} PRIVATE -std=gnu++11)

如果组件的公共头文件也需要以该语言标准编译,请使用 PUBLIC 而非 PRIVATE。

  • 在C代码中调用C++函数
// declaration in the header file:
#ifdef __cplusplus
extern "C" {
#endif

void my_cpp_func(void);

#ifdef __cplusplus
}
#endif

// definition in a .cpp file:
extern "C" void my_cpp_func(void) {
    // ...
}
  • 在C++代码中调用C函数
 // declaration in the header file:
#ifdef __cplusplus
extern "C" {
#endif

void my_c_func(void);

#ifdef __cplusplus
}
#endif

// definition in a .c file:
void my_c_func(void) {
    // ...
}
  • 在main.cpp中定义app_main
extern "C" void app_main()
{
}

C++

继承关系和包含关系

第二集20:30
1.类里面 不加public 默认为private
2._pin 类里面加下划线习惯为private
3.只读操作在后面方法后面加const
4.类::方法 表示为类里面的方法定义

第三集头文件和源文件
1.xx.h叫接口文件,xx.cpp叫实现文件/源文件
2.头文件卫士#ifnodef

第四集 构造函数与析构函数
重载(overload)构造函数的概念
LED::LED(byte pin_param,String color_param):_color(color_param){}//通过:来给_color赋值color_param 模板 ( :要被赋值的对象(值))
析构函数(用来释放内存释放资源)格式:~函数
this是指针;*this是对象
this->可以访问成员变量 或者 成员函数

第五集:
缺省:默认的意思

第七集继承的三种关系:(先讲is a 父子关系,has a 汽车与引擎关系后面说)
this就是个指向自己的指针,写不写好像没啥区别
Class Esp32Board : public DevBoard{} //公开继承就是把父类 DevBoard 的Public和Protected给拷贝过来
Class Esp32Board : private DevBoard{} //私有继承就是把父类 DevBoard 的所有都私有起来(私有后就没法再往下公开继承了)
Class Esp32Board : protected DevBoard{} //保护继承就是把父类 DevBoard 的所有都保护起来(保护后还可以再往下公开继承)
所以说ESP32Board是派生类;DevBoard是基类
基类表

第八集继承的构建函数
1.当你创建了一个类(比如cube),1.内存会分配空间2.会运行类的构建函数cube(),来初始化变量
2.要想访问基类的private里面的变量只能用一个中间方法(把基类的函数拿过来,通过这个函数调用了private)来访问,可以在public里面创建方法
3.当你用对象想来继承类时,只能继承public和protected,想把父类的private的变量也继承过来,就得用在继承函数后面加上:构造函数(私有参数)

子类实例化后一般是不会调用父类的构造函数的,如果需要使用父类的private,可以在子类的构造函数后面用:继承父类的构造函数,构造函数使用private。

第九集多继承(父类有好多个)(不被推荐也不怎么用)
1.构造函数里需要对初始变量赋值
2.多继承的问题就是如果public里面命名相同,包括父类父类的相同,父类子类的相同
3.对象:(继承)类

第十集手搓LGVL
1.C++的优势就是抽象,一般不需要看的详细省去,你写代码用.h就行
2.this->可写可不写
3.制作.cpp源文件需要添加#include “Arduino.h”
4…h放class定义,具体的类::方法放到源文件.cpp里面

复合类和多态

第十一集引用和指针
引用是”别名“,int &ref = v1;
指针是地址
区别
(1)引用初始化必须赋值。
(2)指针可以被重新分配
(3)指针有自己的地址,引用的地址与变量一致。
(4)引用不能为空,甚至不能为常数。要求变量
分别什么时候用?
如果变量值可能会变,那就用指针。

第十二集复合类
类和类之间的关系:复合类(has a)把其中整个类作为自己的数据成员,包含关系
几种写法:
OLED oled;//生成大类的时候同时生成小类
OLED *oled;//生成指针, 可以先不存储,用nullptr,比较灵活,绝大部分用指针
所以说,类定义可以用具体值,也可以用指针

第十三集关联关系

第十四集多态虚函数(function iverloading)
条件:1.继承 2. 指针或引用调用
可以用父类来声明子类

第十五集多态
https://blog.csdn.net/weixin_50569901/article/details/124003064
多态分为静态多态和动态多态
overload重载(静态多态)一个函数通过多个参数来达到不同的形态
动态多态需要使用虚函数
override覆盖,告诉编译器我这个函数要被覆盖。起保险的作用。
redefine重定义,基类不使用虚函数virtual,子类就处于重定义状态,不能实现多态。对象会直接调用基类的函数。
加final说明子类不能对它进行复写

第十六集抽象类(烧腊)和具体类(烧鸭)
有一个以上的纯虚函数的类就是抽象类,抽象类不能具体化 virtual int available() = 0;

第十七集 静态类成员(使用Static)
一般变量不同的类会创造不同的变量,静态类变量是所有的类共用同一个变量。有一个类把静态变量改了,那其他的也会变掉。
静态的成员方法使用的变量也一定是静态的
(在函数外定义,就只能在本文件被使用;在函数内定义,只能在本文件使用)
用extern 就可以全域使用;用static就只能本区域使用,避免了耦合和重入

第十八集 动态分配pointer(使用malloc和new对int数组对象分别是怎么用的)(无操作系统不介意直接使用堆,会造成堆内存碎片化)
动态内存分配在heap里(堆)
静态内存分配在stack里(栈)First in ,last out

Malloc(10)代表给了10个字节 new int(100)代表赋值了100

第十九集( const常量的用法)
从右往左读

第二十一集
Explicit是显性标识符,用在定义前就表明不准隐式转换了。

第二十三/二十四集 操作符重载(operating overloading)
操作符就是±*/,本来这些只是对于有类型的int、char这些来说,但是你对对象弄就错了,所以可以在类里面加操作符重载函数,转换成具体的值的加减乘除之类的
操作符重载的原则,如果你看到操作符却不知道什么意思,要返回到.cpp文件去看,就已经失败了

第二十五集 template
template
特点:<>

第二十六集 inline
本质:拿空间换时间(程序员使用inline建议(只是建议,不是命令)编译器直接用函数主体把调用给替换掉,这样函数占用stack可能多一些,但是快了)
如果在声明里面就写了函数主体,那就是默认的inline,你写不写inline都是inline;如果拿出来定义的话就必须写inline了
最后,现在cpu已经很快了,这个的作用?存疑

第二十七集 回调函数
建立一个回调函数,用函数指针把回调函数调过来
函数名本质上就是个指针

类和结构区别

1.结构体是一种值类型,而类是引用类型。值类型用于存储数据的值,引用类型用于存储对实际数据的引用。
那么结构体就是当成值来使用的,类则通过引用来对实际数据操作。

2.结构使用栈存储(Stack Allocation),而类使用堆存储(Heap Allocation)
栈的空间相对较小.但是存储在栈中的数据访问效率相对较高.
堆的空间相对较大.但是存储在堆中的数据的访问效率相对较低.

3.类是反映现实事物的一种抽象,而结构体的作用只是一种包含了具体不同类别数据的一种包装,结构体也可以继承,也可以有函数(c中结构体没有函数),但是不具备类的继承多态特性

4.结构体赋值是 直接赋值的值. 而对象的赋值的是传递对象的地址

5.结构体内默认“数据“访问权限和继承权限是public,对象内“”成员“”默认访问权限和继承权限是private。结构体和类可以交叉继承,继承权限取决于子类而不是基类。

指向结构体成员运算符
指向结构体或对象的指针->其内成员

C与C++字符串相互转换

char c = ‘’; //C语言字符定义
Char c[]=””; //C语言没有所谓的字符串,一般用字符数组和字符指针代替
Char *c=’’;
String c=””;//C++定义字符串
两者转换
String.c_str() //可以转换成const *C

#include
#include
using namespace std;
int main()
{
char ch = ‘X’;
string s ;
char s1[2] = {ch, 0};//定义一个字符数组,即传统的字符串,使其值为单个字符加上字符串结束符\0。
s = s1;//将字符赋值给string对象。
cout << s << endl;
}

Pimpl(指针到实现)

在C++编程中,Pimpl(Pointer to IMPLementation)模式是一种常见的设计技术,主要用于封装类的实现细节,从而提高编译效率和封装性。简单说,就是通过一个指针简化了一个类的实现细节,帮助减少头文件的依赖,从而减少不必要的编译。

  • 基本概念

Pimpl模式涉及两个主要部分:一个是对外提供接口的类(通常称为接口类),另一个是实际执行具体实现的类(通常称为实现类或Impl类)。

接口类:包含一个指向实现类对象的指针。接口类的头文件中只有这个指针的声明,没有实现类的任何细节。
实现类:在源文件中定义,包含了实际的数据成员和成员函数。这个类不会在接口类的头文件中暴露。

  • 优势

(1)减少编译依赖:当实现细节更改时,只有实现类的源文件需要重新编译。使用该类的其他代码不需要重新编译,因为接口类的头文件未改变。
(2)增强封装:用户只能通过接口类提供的方法访问实现类,这样可以隐藏实现细节,提高模块的封装性。
(3)改善接口稳定性:接口类的API可以保持稳定,即使实现类的实现细节发生了变化。

  • 劣势

(1)运行时性能开销:每次通过接口类访问实现类时,都需要通过指针间接访问,这会带来轻微的性能开销。
(2)内存管理复杂性:需要确保正确地管理实现类对象的生命周期,防止内存泄漏。

示例

// File: AudioPlayer.h
class AudioPlayerImpl; // 前向声明

class AudioPlayer {
public:
    AudioPlayer();
    ~AudioPlayer();
    AudioPlayer(const AudioPlayer&);            // Copy constructor
    AudioPlayer& operator=(const AudioPlayer&); // Copy assignment operator

    void play();
    void stop();

private:
    AudioPlayerImpl* impl;
};

// File: AudioPlayer.cpp
#include "AudioPlayer.h"
#include "AudioPlayerImpl.h"

class AudioPlayerImpl {
public:
    void play() {
        // 实现播放逻辑
    }
    void stop() {
        // 实现停止逻辑
    }
};

AudioPlayer::AudioPlayer() : impl(new AudioPlayerImpl()) {}

AudioPlayer::~AudioPlayer() { delete impl; }

AudioPlayer::AudioPlayer(const AudioPlayer& other) : impl(new AudioPlayerImpl(*other.impl)) {}

AudioPlayer& AudioPlayer::operator=(const AudioPlayer& other) {
    if (this != &other) {
        *impl = *other.impl;
    }
    return *this;
}

void AudioPlayer::play() { impl->play(); }
void AudioPlayer::stop() { impl->stop(); }

数据的大端与小端

大端(Big-endian):

  • 在大端模式下,一个多字节值的最高位字节(即“大端”)存储在最低的内存地址处,其余字节按照大小递减的顺序存储。
  • 这种存储方式类似于人类书写和阅读多字节数字的方式,即从最高位到最低位。
  • 大端模式的计算机在处理网络协议时通常需要较少的转换,因为网络协议通常使用大端字节序。

小端(Little-endian):

  • 在小端模式下,一个多字节值的最低位字节(即“小端”)存储在最低的内存地址处,其余字节按照大小递增的顺序存储。
  • 这种存储方式在现代计算机中更为常见,包括Intel和AMD的x86架构。
  • 小端模式的计算机在处理网络协议时可能需要进行字节序转换。

例如,假设我们有以下三个16位整数:0xABCD, 0xEF01, 0x2345。

在大端模式下,它们的存储顺序将是:
地址1: 0xAB
地址2: 0xCD
地址3: 0xEF
地址4: 0x01
地址5: 0x23
地址6: 0x45

在小端模式下,它们的存储顺序将是:
地址1: 0xCD
地址2: 0xAB
地址3: 0x01
地址4: 0xEF
地址5: 0x45
地址6: 0x23

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值