如何遍历模板化MenuNode类的链表结构?
通用LCD菜单模板类改造方案
我正在为4行LCD开发通用菜单类,现有非模板版本仅支持int类型数据,希望扩展为模板类以支持int、float、unsigned等多种类型。但直接改为模板类后,不同类型的MenuNode实例属于不同类型,无法用统一指针遍历菜单结构,核心问题是如何实现跨类型的菜单遍历。
核心解决方案:分离导航逻辑与数据类型
通过非模板基类+模板子类的结构解决问题:
- 定义非模板基类,封装所有菜单导航相关的成员(父/子/兄弟节点指针、提示文本、导航方法),与数据类型完全无关;
- 模板子类继承基类,专门处理特定类型的数据存储与读写;
- 利用虚函数实现不同类型值的统一输出,确保遍历逻辑无需关心具体数据类型。
1. 定义非模板基类 MenuNodeBase
这个基类负责所有导航逻辑,是所有菜单节点的统一接口:
/* * 菜单节点基类:处理导航逻辑,与数据类型无关 */ class MenuNodeBase { private: char* prompt; public: MenuNodeBase* parent = NULL; MenuNodeBase* child = NULL; MenuNodeBase* prevSibling = NULL; MenuNodeBase* nextSibling = NULL; char* Prompt() { return prompt; } MenuNodeBase(char* prompt, MenuNodeBase* parent, MenuNodeBase* prevSibling) { Serial.print(prompt); Serial.println(F(" MenuNodeBase")); this->prompt = prompt; if (prevSibling != NULL) { this->prevSibling = prevSibling; prevSibling->SetNextSibling(this); this->parent = prevSibling->parent; } if (prevSibling == NULL && parent != NULL) { this->parent = parent; this->parent->SetChild(this); } } void SetChild(MenuNodeBase* child) { Serial.print(prompt); Serial.println(F(" SetChild")); this->child = child; } void SetNextSibling(MenuNodeBase* nextSibling) { Serial.print(prompt); Serial.println(F(" SetNextSibling")); this->nextSibling = nextSibling; } // 纯虚函数,强制子类实现值的打印逻辑 virtual void PrintValue() = 0; };
2. 定义模板子类 MenuNode<T>
继承基类,添加类型安全的数据读写方法:
/* * 模板菜单节点类:处理特定类型的数据 */ template<typename T> class MenuNode : public MenuNodeBase { private: T value; public: MenuNode(char* prompt, T initialValue, MenuNodeBase* parent, MenuNodeBase* prevSibling) : MenuNodeBase(prompt, parent, prevSibling) { value = initialValue; } void SetValue(T value) { this->value = value; } T GetValue() { return value; } // 实现基类的虚函数,适配当前类型的打印 void PrintValue() override { Serial.print(value); } };
3. 修改遍历打印函数
使用基类指针MenuNodeBase*遍历,通过虚函数PrintValue()自动适配不同类型的值:
void PrintMenuStructure(MenuNodeBase* node, int offset) { while (node != NULL) { for (int i = 0; i < offset; i++) Serial.print("-"); Serial.print(node->Prompt()); Serial.print(" = "); node->PrintValue(); // 调用虚函数,自动匹配数据类型 if (node->parent != NULL) { Serial.print(" parent="); Serial.print(node->parent->Prompt()); } if (node->prevSibling != NULL) { Serial.print(" prevSib="); Serial.print(node->prevSibling->Prompt()); } if (node->nextSibling != NULL) { Serial.print(" nextSib="); Serial.print(node->nextSibling->Prompt()); } if (node->child != NULL) { Serial.print(" child="); Serial.print(node->child->Prompt()); } Serial.println(); if (node->child != NULL) PrintMenuStructure(node->child, offset + 1); node = node->nextSibling; } }
4. 混合类型测试代码
现在可以创建不同数据类型的菜单节点,且能正常遍历:
// 测试菜单:混合int、float、unsigned类型 MenuNode<int> r1("R1", 10, NULL, NULL); MenuNode<float> r2("R2", 20.5f, NULL, &r1); MenuNode<unsigned> r21("R21", 30u, &r2, NULL); MenuNode<int> r22("R22", 40, &r2, &r21); MenuNode<float> r221("R221", 50.2f, &r22, NULL); MenuNode<unsigned> r2211("R2211", 60u, &r221, NULL); MenuNode<int> r2212("R2212", 70, NULL, &r2211); MenuNode<float> r3("R3", 30.8f, NULL, &r2); // 启动遍历打印 PrintMenuStructure(&r1, 0);
关键说明
- 基类
MenuNodeBase确保所有节点可以通过统一指针访问导航逻辑,解决了模板类实例类型不兼容的问题; - 模板子类
MenuNode<T>提供类型安全的数据操作,虚函数PrintValue()实现了遍历过程中的统一输出; - 如果需要对特定类型节点做针对性操作,可以使用
dynamic_cast<MenuNode<T>*>(node)进行类型转换(需确保类型匹配)。
内容的提问来源于stack exchange,提问作者GHamsz
相关产品推荐
相关产品推荐

