C++ 使用正确的 child-class 指针
C++ Using proper child-class pointer
基本上,我想实现文档类型转换器。我设计了漂亮的 straight-forward 解决方案:
DocTypeParser : Parser
将文件转换为节点的树结构,代表不同的元素(headers、列表、粗体文本,...)
DocTypePrinter : Printer
会将树解构回文本文件
到目前为止一切顺利,但我遇到了一个棘手的问题 - 树节点之间的连接是通过 std::vector<Node *>
建立的,我不确定如何确定 child class 是什么正在处理中。
我的演示代码:
class Node
{
public:
Node()
{
}
~Node()
{
for (auto it : Leaf)
delete it;
}
Node &Add(Node *leaf)
{
Leaf.push_back(leaf);
return *this;
}
std::vector<Node *> Leaf;
};
class NodeA : public Node
{
public:
NodeA() : Node()
{
}
};
class Printer
{
public:
Printer() = default;
std::string Print(Node &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<n>";
for (; i < k; ++i)
res += Print(*(n.Leaf[i]));
res += "</n>";
return res;
}
std::string Print(NodeA &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<A>";
for (; i < k; ++i)
res += Print(*(n.Leaf[i]));
res += "</A>";
return res;
}
};
int main(int argc, const char *argv[])
{
NodeA tree;
tree.Add(new NodeA).Add(new NodeA);
Printer p;
std::cout << p.Print(tree) << std::endl;
return 0;
}
想要的结果: <A><A></A><A></A></A>
实际结果: <A><n></n><n></n></A>
我非常理解问题所在(向量存储 Node
指针,而不是 NodeChild
指针),但不确定如何克服它。 dynamic_cast
好像是 not-the-solution-at-all.
所以最后问题 - 我有治愈方法还是我完全渴望错误的设计?
你错误地使用了类型擦除。 Node*
访问了您的节点,因此 *(n.Leaf[i])
表达式 returns 类型 Node
,而不是 NodeA
。
你所做的类似于访问者模式,要识别哪个 class 是你必须在 Node class 中使用虚方法并在 NodeA 中覆盖它,使用调度程序作为参数调用它(classic visitor) 或者从调度程序调用它,你可以识别哪个实例是哪个。
在第一种情况下,节点会调用 Print 方法并传递它 *this
。
这是对您的代码进行的最少修改,但我认为,它需要 honing\optimizing。看你的实际任务是什么,vistor可能有点过分了。
#include <string>
#include <iostream>
#include <vector>
class Node;
class NodeA;
class AbstractPrinter
{
public:
virtual std::string Print(Node &n) =0;
virtual std::string Print(NodeA &n) =0;
};
class Node
{
public:
Node()
{
}
virtual ~Node()
{
for (auto it : Leaf)
delete it;
}
Node &Add(Node *leaf)
{
Leaf.push_back(leaf);
return *this;
}
virtual std::string Print(AbstractPrinter& p)
{
return p.Print(*this);
}
std::vector<Node *> Leaf;
};
class NodeA : public Node
{
public:
NodeA() : Node()
{
}
// if not override this, it would use Node
virtual std::string Print(AbstractPrinter& p) override
{
return p.Print(*this);
}
};
class Printer : public AbstractPrinter
{
public:
Printer() = default;
std::string Print(Node &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<n>";
for (; i < k; ++i)
res += n.Leaf[i]->Print(*this);
res += "</n>";
return res;
}
std::string Print(NodeA &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<A>";
for (; i < k; ++i)
res += n.Leaf[i]->Print(*this);
res += "</A>";
return res;
}
};
int main(int argc, const char *argv[])
{
NodeA tree;
tree.Add(new NodeA).Add(new NodeA);
Printer p;
std::cout << tree.Print(p) << std::endl;
return 0;
}
基本上,我想实现文档类型转换器。我设计了漂亮的 straight-forward 解决方案:
DocTypeParser : Parser
将文件转换为节点的树结构,代表不同的元素(headers、列表、粗体文本,...)DocTypePrinter : Printer
会将树解构回文本文件
到目前为止一切顺利,但我遇到了一个棘手的问题 - 树节点之间的连接是通过 std::vector<Node *>
建立的,我不确定如何确定 child class 是什么正在处理中。
我的演示代码:
class Node
{
public:
Node()
{
}
~Node()
{
for (auto it : Leaf)
delete it;
}
Node &Add(Node *leaf)
{
Leaf.push_back(leaf);
return *this;
}
std::vector<Node *> Leaf;
};
class NodeA : public Node
{
public:
NodeA() : Node()
{
}
};
class Printer
{
public:
Printer() = default;
std::string Print(Node &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<n>";
for (; i < k; ++i)
res += Print(*(n.Leaf[i]));
res += "</n>";
return res;
}
std::string Print(NodeA &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<A>";
for (; i < k; ++i)
res += Print(*(n.Leaf[i]));
res += "</A>";
return res;
}
};
int main(int argc, const char *argv[])
{
NodeA tree;
tree.Add(new NodeA).Add(new NodeA);
Printer p;
std::cout << p.Print(tree) << std::endl;
return 0;
}
想要的结果: <A><A></A><A></A></A>
实际结果: <A><n></n><n></n></A>
我非常理解问题所在(向量存储 Node
指针,而不是 NodeChild
指针),但不确定如何克服它。 dynamic_cast
好像是 not-the-solution-at-all.
所以最后问题 - 我有治愈方法还是我完全渴望错误的设计?
你错误地使用了类型擦除。 Node*
访问了您的节点,因此 *(n.Leaf[i])
表达式 returns 类型 Node
,而不是 NodeA
。
你所做的类似于访问者模式,要识别哪个 class 是你必须在 Node class 中使用虚方法并在 NodeA 中覆盖它,使用调度程序作为参数调用它(classic visitor) 或者从调度程序调用它,你可以识别哪个实例是哪个。
在第一种情况下,节点会调用 Print 方法并传递它 *this
。
这是对您的代码进行的最少修改,但我认为,它需要 honing\optimizing。看你的实际任务是什么,vistor可能有点过分了。
#include <string>
#include <iostream>
#include <vector>
class Node;
class NodeA;
class AbstractPrinter
{
public:
virtual std::string Print(Node &n) =0;
virtual std::string Print(NodeA &n) =0;
};
class Node
{
public:
Node()
{
}
virtual ~Node()
{
for (auto it : Leaf)
delete it;
}
Node &Add(Node *leaf)
{
Leaf.push_back(leaf);
return *this;
}
virtual std::string Print(AbstractPrinter& p)
{
return p.Print(*this);
}
std::vector<Node *> Leaf;
};
class NodeA : public Node
{
public:
NodeA() : Node()
{
}
// if not override this, it would use Node
virtual std::string Print(AbstractPrinter& p) override
{
return p.Print(*this);
}
};
class Printer : public AbstractPrinter
{
public:
Printer() = default;
std::string Print(Node &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<n>";
for (; i < k; ++i)
res += n.Leaf[i]->Print(*this);
res += "</n>";
return res;
}
std::string Print(NodeA &n)
{
int i = 0, k = n.Leaf.size();
std::string res = "<A>";
for (; i < k; ++i)
res += n.Leaf[i]->Print(*this);
res += "</A>";
return res;
}
};
int main(int argc, const char *argv[])
{
NodeA tree;
tree.Add(new NodeA).Add(new NodeA);
Printer p;
std::cout << tree.Print(p) << std::endl;
return 0;
}