F´ 框架 ExternalStack 完全指南:使用外部存储的 LIFO 栈模板 📅 发布时间:2026/9/15 12:22:36 👁 浏览次数: F´ 框架 ExternalStack 完全指南使用外部存储的 LIFO 栈模板【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprimeExternalStack是 F´ 飞行软件与嵌入式系统框架中定义于Fw/DataStructures基础数据结构库的一个final类模板它实现了一个使用外部存储的后进先出LIFO栈。本文以 ExternalStack.md 为骨架结合其头文件实现与单元测试系统讲解该类模板的模板参数、继承关系、全部构造函数、成员函数、静态辅助函数及测试验证帮助你在 F´ 组件中正确地用它管理静态分配的内存与栈式数据。1. 概览什么是 ExternalStackExternalStack表示一个带有外部存储的栈栈本身不持有元素数据的内存而是通过内部维护的一个 ExternalArray 来引用调用者提供的后备存储backing storage。这种外部存储设计在嵌入式/飞行软件中非常实用——内存往往由上层系统静态分配或通过内存池统一管理数据结构本身只负责组织与访问逻辑。从 ExternalStack.hpp 的声明可以看出它是一个模板类并被标记为finaltemplate typename T class ExternalStack final : public StackBaseT {final关键字意味着它不能被继续继承配合完整的接口实现保证了栈语义的封闭与确定。2. 类层次与依赖关系ExternalStackT公开继承自StackBase而StackBase又是 SizedContainer 的派生类见 StackBase.hpp。三者共同构成 F´ 栈数据结构家族的抽象骨架各层职责划分如下SizedContainer最顶层的抽象容器接口定义了clear()、getSize()、getCapacity()三个纯虚函数并提供基于它们的isEmpty()与isFull()便捷方法见 SizedContainer.hpp。它删除了拷贝构造与赋值运算符将如何复制交给具体实现决定。StackBaseT栈的抽象基类模板将at、push、pop声明为纯虚函数并基于这些原语实现了非虚的peek带默认索引 0 的窥视与copyDataFrom跨栈数据拷贝方法见 StackBase.hpp。ExternalStackT栈接口的具体实现内部组合一个ExternalArrayT来持有元素。这里有一个值得注意的细节StackBase虽然删除了拷贝构造和operator见 StackBase.hpp但ExternalStack通过浅拷贝语义自行实现了拷贝构造与赋值——复制的是对外部数组的引用指针与容量而非元素数据。这点将在第 5 节详细展开。3. 模板参数ExternalStack只有一个模板参数KindNamePurposetypenameT栈中元素item的类型对T有一个静态约束ExternalArrayT的static_assert(std::is_assignableT, T::value, ...)要求T可赋值见 ExternalArray.hpp因为栈操作push、pop依赖对T的赋值。4. 私有成员变量ExternalStack内部只有两个私有成员见 ExternalStack.hppNameTypePurposeDefault Valuem_itemsExternalArrayT存储栈元素的数组C 默认初始化{}即空指针 0 容量m_sizeFwSizeType栈中当前元素数量0m_items是外部存储的载体它自身只保存指向元素内存的指针m_elements与容量m_size见 ExternalArray.hpp并不拥有内存的所有权语义上的分配m_size则记录当前栈顶的位置与m_items的容量相互配合。FwSizeType是 F´ 框架定义的无符号大小类型。5. 构造函数与析构函数ExternalStack提供四个构造函数与一个默认析构函数覆盖了先构造后设存储构造时绑定存储无类型字节存储与拷贝四种典型用法。5.1 零参数构造函数ExternalStack()将所有成员初始化为默认值见 ExternalStack.hpp此时栈为空、容量为 0不能直接进行push操作需要先调用setStorage绑定后备存储。示例ExternalStackU32 stack; // 容量为 0大小为 05.2 提供类型化后备存储的构造函数ExternalStack(T* items, FwSizeType capacity)items必须指向一个至少包含capacity个T类型元素的原生数组。该构造函数执行调用setStorage(items, capacity)其余成员变量初始化为默认值。示例constexpr FwSizeType capacity 10; U32 items[capacity]; ExternalStackU32 stack(items, capacity);5.3 提供无类型后备存储的构造函数ExternalStack(ByteArray data, FwSizeType capacity)当内存以字节形式如内存池中的缓冲提供时使用。data必须满足两个约束按getByteArrayAlignment()返回的对齐值对齐至少包含getByteArraySize(capacity)字节。其实现为调用setStorage(data, capacity)其余成员默认初始化。示例constexpr FwSizeType capacity 10; constexpr U8 alignment ExternalStackU32::getByteArrayAlignment(); constexpr FwSizeType byteArraySize ExternalStackU32::getByteArraySize(capacity); alignas(alignment) U8 bytes[byteArraySize]; ExternalStackU32 stack(ByteArray(bytes[0], sizeof bytes), capacity);5.4 拷贝构造函数ExternalStack(const ExternalStackT stack)执行*this stack见 ExternalStack.hpp。与常规深拷贝元素不同这里复制的是对外部数组的引用目标栈将共享源栈的后备存储指针与容量同时复制m_size。示例constexpr FwSizeType capacity 3; U32 items[capacity]; // 调用提供后备存储的构造函数 ExternalStackU32 q1(items, capacity); // 压入一个元素 U32 value 42; (void) q1.push(value); // 调用拷贝构造函数 ExternalStackU32 q2(q1); ASSERT_EQ(q2.getSize(), 1);5.5 析构函数~ExternalStack() override定义为 default见 ExternalStack.hpp。它不释放后备存储因为存储不属于栈所有。6. 核心成员函数6.1 operator拷贝赋值ExternalStackT operator(const ExternalStackT stack)语义与拷贝构造一致算法为见 ExternalStack.hpp若stack ! this令m_items stack.m_itemsExternalArray的赋值会重新指向源数组的指针与容量令m_size stack.m_size返回*this。示例constexpr FwSizeType capacity 3; U32 items[capacity]; // 调用提供后备存储的构造函数 ExternalStackU32 q1(items, capacity); // 压入一个元素 U32 value 42; (void) q1.push(value); // 调用默认构造函数 ExternalStack q2; ASSERT_EQ(q2.getSize(), 0); // 调用拷贝赋值运算符 q2 q1; ASSERT_EQ(q2.getSize(), 1);6.2 clearvoid clear() override将m_size置为 0见 ExternalStack.hpp。注意它只重置逻辑大小不清空或释放后备存储中的元素内容。示例constexpr FwSizeType capacity 10; U32 items[capacity]; ExternalStackU32 stack(items, capacity); const auto status stack.push(3); ASSERT_EQ(stack.getSize(), 1); stack.clear(); ASSERT_EQ(stack.getSize(), 0);6.3 setStorage类型化数据void setStorage(T* items, FwSizeType capacity)items必须指向至少capacity个T元素的原生数组。算法见 ExternalStack.hpp调用m_items.setStorage(items, capacity)调用this-clear()。在底层ExternalArray::setStorage会先断言非空数组时指针不能为空并在传入指针与既有存储指针相同时避免释放存储见 ExternalArray.hpp。示例constexpr FwSizeType capacity 10; ExternalStackU32 stack; // 先默认构造 U32 items[capacity]; stack.setStorage(items, capacity);6.4 setStorage无类型数据void setStorage(ByteArray data, FwSizeType capacity)data需满足与 5.3 相同的对齐与字节数约束。算法为调用m_items.setStorage(data, capacity)再调用this-clear()。在底层ExternalArray::setStorage的字节版本会依次断言字节指针非空、指针按alignof(T)对齐、size data.size / sizeof(T)采用除法形式避免size * sizeof(T)溢出FwSizeType随后就地 placement-new 构造每个元素见 ExternalArray.hpp确保每个元素都是合法对象可供后续赋值。示例constexpr FwSizeType capacity 10; constexpr U8 alignment ExternalStackU32::getByteArrayAlignment(); constexpr FwSizeType byteArraySize ExternalStackU32::getByteArraySize(capacity); alignas(alignment) U8 bytes[byteArraySize]; ExternalStackU32 stack; // 先默认构造 stack.setStorage(ByteArray(bytes[0], sizeof bytes), capacity);6.5 pushSuccess push(const T e) override将元素压入栈顶。算法见 ExternalStack.hpp令status Success::FAILURE若m_size getCapacity()令m_items[m_size] e递增m_size令status Success::SUCCESS返回status。栈满时push返回Success::FAILURE而不是触发断言这是嵌入式友好接口的典型设计——调用方可以安全地根据返回值处理溢出。示例constexpr FwSizeType capacity 3; U32 items[capacity]; ExternalStackU32 stack(items, capacity); ASSERT_EQ(stack.getSize(), 0); auto status stack.push(42); ASSERT_EQ(status, Success::SUCCESS); ASSERT_EQ(stack.getSize(), 1);6.6 popSuccess pop(T e) override从栈顶弹出元素。算法见 ExternalStack.hpp令status Success::FAILURE若m_size 0令e this-at(0)取最右侧、即最新元素递减m_size令status Success::SUCCESS返回status。对空栈调用pop同样返回FAILURE不会崩溃。示例constexpr FwSizeType capacity 3; U32 items[capacity]; ExternalStackU32 stack(items, capacity); U32 val; auto status stack.pop(val); ASSERT_EQ(status, Success::FAILURE); // 空栈 status stack.push(42); ASSERT_EQ(status, Success::SUCCESS); status stack.pop(val); ASSERT_EQ(status, Success::SUCCESS); ASSERT_EQ(val, 42);6.7 atconst T at(FwSizeType index) const override按索引访问元素。索引语义在 StackBase 中明确规定索引 0 是栈最右侧最新压入的元素索引递增方向从右往左见 ExternalStack.hpp。算法断言index m_size越界直接触发FW_ASSERT返回m_items[m_size - 1 - index]。示例constexpr FwSizeType capacity 3; U32 items[capacity]; ExternalStackU32 stack(items, capacity); const auto status stack.push(3); ASSERT_EQ(status, Success::SUCCESS); ASSERT_EQ(stack.at(0), 3); ASSERT_DEATH(stack.at(1), Assert); // 越界断言6.8 getSizeFwSizeType getSize() const override返回当前栈中元素数量m_size。示例constexpr FwSizeType capacity 10; U32 items[capacity]; ExternalStackU32 stack(items, capacity); auto size stack.getSize(); ASSERT_EQ(size, 0); const auto status stack.push(3); ASSERT_EQ(status, Success::SUCCESS); size stack.getSize(); ASSERT_EQ(size, 1);6.9 getCapacityFwSizeType getCapacity() const override返回栈的容量即m_items.getSize()见 ExternalStack.hpp。示例constexpr FwSizeType capacity 10; U32 items[capacity]; ExternalStackU32 stack(items, capacity); ASSERT_EQ(stack.getCapacity(), capacity);6.10 继承自基类的便捷方法除上述覆写外ExternalStack还直接继承了两个由StackBase实现的方法peek(T e, FwSizeType index 0) const窥视指定索引元素而不弹出索引越界时返回Success::FAILURE见 StackBase.hpp。copyDataFrom(const StackBaseT stack)将另一栈的元素拷贝到当前栈取两者 size/capacity 的较小者逐元素push并断言成功见 StackBase.hpp。7. 公共静态函数7.1 getByteArrayAlignmentstatic constexpr U8 getByteArrayAlignment()返回ExternalArrayT::getByteArrayAlignment()即alignof(T)见 ExternalArray.hpp。用于计算无类型后备存储所需的对齐。7.2 getByteArraySizestatic constexpr FwSizeType getByteArraySize(FwSizeType capacity)返回ExternalArrayT::getByteArraySize(capacity)即capacity * sizeof(T)见 ExternalArray.hpp。用于计算无类型后备存储所需的字节数。两个函数均为constexpr可在编译期用于静态数组声明见 5.3 的示例。8. 测试验证行为规格的机器可读化ExternalStack的行为不仅由文档描述还有完整的单元测试在 test/ut/ExternalStackTest.cpp 中固化为可执行断言构造测试ZeroArgConstructor验证默认构造容量/大小均为 0TypedStorageConstructor与UntypedStorageConstructor分别验证两种后备存储构造函数正确接管了外部数组指针、容量正确且初始大小 0见 ExternalStackTest.cpp。拷贝语义测试CopyConstructor与CopyAssignmentOperator验证拷贝后getSize() 1且共享同一后备存储指针CopyDataFrom则验证size1 capacity2、size1 capacity2、size1 capacity2三种跨容量拷贝场景见 ExternalStackTest.cpp。场景测试基于 STest 规则/场景框架分别覆盖at、clear、peek、popEmpty、popOK、pushFull、pushOK并提供一个执行 1000 次随机操作的Random场景见 ExternalStackTest.cpp。测试辅助类 ExternalStackTester.hpp 通过friend声明访问了ExternalStack的私有成员m_items见 ExternalStack.hpp从而可以断言内部ExternalArray的具体状态。此外内部存储版本StackT, C的测试 test/ut/StackTest.cpp 同样基于相同语义可与ExternalStack的测试互为印证。9. 使用场景与注意事项典型使用场景在 F´ 的 active/queued 组件内部用ExternalStack保存临时待处理项后备存储来自静态数组或内存池缓冲区。F´ 官方文档 sdd.md 同时指出Fw/DataStructures中的数据结构都是**顺序型非线程安全**的在多线程上下文中使用时必须借助外部并发控制——最常见做法是将数据结构作为 active/queued 组件的成员用组件队列来串行化对其的访问。关键注意事项存储所有权ExternalStack不拥有后备存储析构、clear均不释放内存释放内存的责任始终在调用方。拷贝是引用共享拷贝构造/赋值复制的是对外部数组的引用而非元素两个栈会指向同一后备存储修改一侧会影响另一侧StackBase中删除拷贝构造的注释也明确写着行为取决于实现。越界即断言at越界会触发FW_ASSERT在单元测试中对应ASSERT_DEATH而push满栈、pop空栈则通过返回值Success::FAILURE优雅处理使用时务必检查返回值。字节存储的约束无类型存储必须满足对齐与最小字节数两个硬约束违反会在setStorage阶段触发断言。结合 ExternalArray 的完整实现ExternalStack构成了 F´ 基础数据结构库中栈 外部内存的参考实现是理解整个 Fw/DataStructures 库内部存储/外部存储双轨设计的最佳入口之一。【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考