C ++ Использование правильного указателя на дочерний класс - PullRequest
0 голосов
/ 11 мая 2018

В основном я хочу реализовать конвертер типов документов.Я разработал довольно простое решение:

  1. DocTypeParser : Parser преобразует файл в древовидную структуру узлов, представляющих различные элементы (заголовки, списки, полужирный текст, ...)
  2. DocTypePrinter : Printer деконструирует это дерево обратно в текстовый файл

Пока все хорошо, но я столкнулся с неприятной проблемой - соединение между узлами дерева устанавливается через std::vector<Node *>, и я не уверенкак определить, какой дочерний класс обрабатывается.

Мой демонстрационный код:

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 кажется не лучшим решением для всех.

Итак, наконец вопрос - есть ли лекарство для меня или я вообще жажду неправильного дизайна?

1 Ответ

0 голосов
/ 11 мая 2018

Вы использовали стирание типа неправильно. Ваши узлы доступны с помощью Node*, поэтому выражение *(n.Leaf[i]) возвращает тип Node, а не NodeA.

То, что вы делаете, напоминает шаблон посетителя, чтобы распознать, какой класс вам нужен, вы должны использовать виртуальный метод в классе Node и переопределить его в NodeA, вызывая его с диспетчером в качестве аргумента (классический посетитель) или вызывая его из диспетчера, который вы можете распознать. какой экземпляр какой.

В первом случае узел будет вызывать метод Print и передавать его *this.

Это минимальная переделка вашего кода, но я думаю, что он требует доработки \ оптимизации. Зависит от того, что вы на самом деле делаете, может быть, 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;
}
...