避免在树结构中使用铸造
Avoid using cast in tree structure
我们正在考虑一种需要铸造的设计,这是不正确的。显然,谷歌搜索了这个问题,但没有找到答案。将感谢有关如何避免铸造需求的建议。这是一个被剥离的示例(请执行任何错别字,尚未编译)。
struct NodeBase
{
enum class type
{
value,
aggregate
};
type m_type;
};
struct NodeAggregate : public NodeBase
{
std::vector<NodeBase*> m_list;
};
struct NodeValue : public NodeBase
{
std::string m_key;
std::string m_value;
};
上面的类可用于创建具有多个级别的树结构。
"困难"是开发一种算法,该算法在不进行铸造的情况下遍历了这种结构。基类中的类型变量应识别正确的类型,并将铸件的数量减少到单个,但不能避免铸造。
这个问题的替代设计是什么?
感谢任何评论。
您似乎要重新实现的模式称为标记的联合或变体,通常与访问者模式配对。我建议使用现有的实现,而不是自己滚动。
,但也考虑替代实现:
-
使用均匀节点。让每个节点都能存储两个子列表和数据。这样,您只需要一种类型的节点,而无需铸造。如果只有叶子才能有数据,那么您可以在算法中实现该限制,而不是数据结构。
struct Node { std::vector<Node*> m_list; std::string m_key; std::string m_value; };
-
或使用虚拟函数:
struct NodeBase { virtual bool is_leaf() = 0; virtual const range children() const = 0; virtual range children() = 0; virtual const std::string* key() const = 0; virtual std::string* key() = 0; // if the tree is not sorted by key virtual const std::string* value() const = 0; virtual std::string* value() = 0; virtual ~NodeBase() {} };
叶子和分支节点可以以不同的方式实现功能。Leaf总是可以返回空范围,而分支可以返回空键和值。另外,他们可以要求用户使用
is_leaf
,如果称为错误类型的功能。我使用的是一对迭代器,而不是直接返回向量访问向量,它可以使您可以封装基础容器的选择。
在所有这些设计中,您都可以将密钥和值类型用于更好的通用性。
如果您有几种节点类型考虑使用访问者模式在没有铸造的情况下穿越所有树节点:
#include <iostream>
#include <memory>
#include <string>
class Aggregate;
class ConcreteNode1;
class ConcreteNode2;
class Visitor {
public:
virtual void Visit(ConcreteNode1& node) = 0;
virtual void Visit(ConcreteNode2& node) = 0;
virtual void Start(Aggregate& aggregate) = 0;
virtual void Finish(Aggregate& aggregate) = 0;
};
class Node {
friend class Aggregate;
public:
Node() : parent_(nullptr) {}
virtual ~Node() = default;
virtual void Accept(Visitor& visitor) = 0;
private:
void SetParent(Aggregate* parent) {
parent_ = parent;
}
Aggregate* parent_;
};
class Aggregate : public Node {
public:
void Add(std::shared_ptr<Node> node) {
node->SetParent(this);
nodes_.push_back(std::move(node));
}
virtual void Accept(Visitor& visitor) override {
visitor.Start(*this);
for (auto node : nodes_) {
node->Accept(visitor);
}
visitor.Finish(*this);
}
private:
std::vector<std::shared_ptr<Node>> nodes_;
};
class ConcreteNode1 : public Node {
public:
ConcreteNode1(int data) : data_(data) {}
virtual void Accept(Visitor& visitor) override {
visitor.Visit(*this);
}
int GetData() const { return data_; }
private:
int data_;
};
class ConcreteNode2 : public Node {
public:
ConcreteNode2(std::string name) : name_(std::move(name)) {}
virtual void Accept(Visitor& visitor) override {
visitor.Visit(*this);
}
const std::string& GetName() const { return name_; }
private:
std::string name_;
};
int main()
{
class SimpleVisitor : public Visitor {
virtual void Visit(ConcreteNode1& node) override {
std::cout << "ConcreteNode1: " << node.GetData() << std::endl;
}
virtual void Visit(ConcreteNode2& node) override {
std::cout << "ConcreteNode2: " << node.GetName() << std::endl;
}
virtual void Start(Aggregate& aggregate) override {
std::cout << "Start Aggregaten";
}
virtual void Finish(Aggregate& aggregate) override {
std::cout << "Finish Aggregaten";
}
} visitor;
auto root = std::make_shared<Aggregate>();
root->Add(std::make_shared<ConcreteNode1>(1));
{
auto subtree = std::make_shared<Aggregate>();
subtree->Add(std::make_shared<ConcreteNode1>(2));
subtree->Add(std::make_shared<ConcreteNode2>("test1"));
root->Add(subtree);
}
root->Add(std::make_shared<ConcreteNode2>("test2"));
/// traverse through all nodes
root->Accept(visitor);
}
struct NodeBase;
struct NodeAggregate {
std::vector<std::unique_ptr<NodeBase>> children;
~NodeAggregate();
};
struct NodeValue {
std::string key, value;
};
struct NodeBase {
boost::variant<NodeAggregate, NodeValue> m;
};
inline NodeAggregate::~NodeAggregate() = default;
变体支持访问,这是一种类型的伪静态。
这也消除了所有不必要的间接。
我认为您想要访客模式。例如:
struct NodeAggregate;
struct NodeValue;
struct Visitor
{
virtual void visit(NodeAggregate &aggregate) = 0;
virtual void visit(NodeValue &value) = 0;
};
struct NodeBase
{
virtual void accept(Visitor &visitor) = 0;
};
struct NodeAggregate : public NodeBase
{
std::vector<NodeBase*> m_list;
void accept(Visitor &visitor) override
{
visitor.visit(*this);
for(auto base : m_list)
{
base->accept(visitor);
}
}
};
struct NodeValue : public NodeBase
{
std::string m_key;
std::string m_value;
void accept(Visitor &visitor) override
{
visitor.visit(*this);
}
};
struct WriteToConsoleVisitor : Visitor
{
void visit(NodeAggregate &aggregate) override
{
std::cout << "got an aggregate" << std::endl;
}
void visit(NodeValue &value) override
{
std::cout << "got a value. key = " << value.m_key << ", value = " << value.m_value << std::endl;
}
};
这里的诀窍是拥有一个为系统中的每个节点类型具有visit
方法的访问者类,并且要使每个节点类型具有accept
方法,该方法具有CC_4方法,该方法将带动访问者并将其自身传递到访问者中。这是一种在单个调度语言中实施一种称为Double Dispatch的技术,例如C 。
- 如何循环打印顶点结构
- 通过方法访问结构
- 使用不带参数的函数访问结构元素
- 预处理器:插入结构名称中的前一个行号
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 孤立代码块在结构中引发异常
- 有什么方法可以遍历结构吗
- 如何在 C# 中映射双 C 结构指针?
- 如何在C++中使用结构生成映射
- 无法将结构注册为增强几何体3D点
- 多成员Constexpr结构初始化
- C++将文本文件中的数据读取到结构数组中
- 如何重构类层次结构以避免菱形问题
- 如何在C++中序列化结构数据
- std::vector的包装器,使数组的结构看起来像结构的数组
- 没有为自己的结构调用列表推回方法
- 奇怪的结构&GCC&clang(void*返回类型)
- 在 c++ 中拥有一组结构的正确方法是什么?
- vscode g++链路故障:体系结构x86_64的未定义符号
- C++概念:如何使用'concept'检查模板化结构的属性?