C++ 使用正确的 child-class 指针

C++ Using proper child-class pointer

基本上,我想实现文档类型转换器。我设计了漂亮的 straight-forward 解决方案:

  1. DocTypeParser : Parser 将文件转换为节点的树结构,代表不同的元素(headers、列表、粗体文本,...)
  2. 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;
}