c++ - 使用RTTI克隆唯一指针的 vector

标签 c++ unique-ptr

我想使用RTTI克隆唯一指针的 vector 。
当前,有一个抽象基类Node以及派生类ElementTextNodeElement包含一个唯一的Node指针的 vector 。
我能够创建Element类型的对象并将其移到 vector 中。我希望能够克隆Element并将副本推送到 vector 中,但是我在Element的副本构造函数中苦苦挣扎。
这可能吗?如何使用RTTI克隆唯一的指针?有解决这个问题的更好方法吗?

#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Node {
    virtual ~Node() = default;
    virtual std::string toString() const = 0;
};

struct Element : Node {
    Element() = default;
    Element(const Element &element) {
        // clone children
        // for (const auto &child : element.children) children.push_back(std::make_unique</* get RTTI */>(child));
    }
    Element(Element &&) = default;
    std::string toString() const override {
        std::string str = "<Node>";
        for (const auto &child : children) str += child->toString();
        str += "</Node>";
        return str;
    }
    std::vector<std::unique_ptr<Node>> children;
};

struct TextNode : Node {
    std::string toString() const override { return "TextNode"; }
};

int main() {
    Element root;
    Element node;
    node.children.push_back(std::make_unique<TextNode>());
    // This copy doesn't work because I don't know how to implement the copy constructor
    root.children.push_back(std::make_unique<Element>(node));
    root.children.push_back(std::make_unique<Element>(std::move(node)));
    root.children.push_back(std::make_unique<TextNode>());
    
    std::cout << root.toString();
}
实际输出:

<Node><Node></Node><Node>TextNode</Node>TextNode</Node>


预期产量:

<Node><Node>TextNode</Node><Node>TextNode</Node>TextNode</Node>

最佳答案

您要求的内容无法直接使用RTTI完成,但可以通过手动检查类类型来间接完成。由于您只需要检查少量的类,因此可以这样进行:

#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Node {
    virtual ~Node() = default;
    virtual std::string toString() const = 0;
};

struct Element : Node {
    Element() = default;

    Element(const Element &element) {
        // clone children
        for (const auto &child : element.children) {
            Node *n = child.get();
            if (Element *e = dynamic_cast<Element*>(n)) {
                children.push_back(std::make_unique<Element>(*e));
            }
            else if (TextNode *t = dynamic_cast<TextNode*>(n)) {
                children.push_back(std::make_unique<TextNode>(*t));
            }
        }
    }

    Element(Element &&) = default;

    std::string toString() const override {
        std::string str = "<Node>";
        for (const auto &child : children)
            str += child->toString();
        str += "</Node>";
        return str;
    }

    std::vector<std::unique_ptr<Node>> children;
};

struct TextNode : Node {
    std::string toString() const override { return "TextNode"; }
};

int main() {
    Element root;
    Element node;
    node.children.push_back(std::make_unique<TextNode>());
    root.children.push_back(std::make_unique<Element>(node));
    root.children.push_back(std::make_unique<Element>(std::move(node)));
    root.children.push_back(std::make_unique<TextNode>());
    
    std::cout << root.toString();
}
但是,不用说,如果以后添加更多的后代类,这将变得乏味且容易出错。
更好的选择是在clone()中添加一个虚拟的Node方法,然后让ElementTextNode(以及将来的后代)覆盖它来制作自己的副本,例如:
#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Node {
    virtual ~Node() = default;
    virtual std::string toString() const = 0;
    virtual std::unique_ptr<Node> clone() const = 0;
};

struct Element : Node {
    Element() = default;

    Element(const Element &element) {
        // clone children
        for (const auto &child : element.children)
            children.push_back(child->clone());
    }

    Element(Element &&) = default;

    std::string toString() const override {
        std::string str = "<Node>";
        for (const auto &child : children)
            str += child->toString();
        str += "</Node>";
        return str;
    }

    std::unique_ptr<Node> clone() const override {
        return std::make_unique<Element>(*this);
    }

    std::vector<std::unique_ptr<Node>> children;
};

struct TextNode : Node {
    std::string toString() const override { return "TextNode"; }

    std::unique_ptr<Node> clone() const override {
        return std::make_unique<TextNode>(*this);
    }
};

int main() {
    Element root;
    Element node;
    node.children.push_back(std::make_unique<TextNode>());
    root.children.push_back(std::make_unique<Element>(node));
    root.children.push_back(std::make_unique<Element>(std::move(node)));
    root.children.push_back(std::make_unique<TextNode>());
    
    std::cout << root.toString();
}

关于c++ - 使用RTTI克隆唯一指针的 vector ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62686665/

相关文章:

c++ - Qt 应用程序是否需要最终用户提供额外的库?喜欢 C# 和 .NET Framework?

C++ unique_ptr 从派生到基础的转换

c++ - std::unique_ptr 不能被引用——它是一个被删除的函数

c++ - unique_ptr 超出结构范围

c++ - 为派生类的 vector 调用适当的方法

c++ - 使用 BSD 套接字的 HTTP 服务器不关闭连接或与 Windows 浏览器通信

c++ - 将在程序的发布版本中删除对调用断言的函数的调用吗?

c++ - 负零整数

c++ - 为什么不总是将返回值分配给 const 引用?

c++ - 从工厂方法返回的 const unique_ptr 不需要空检查吗?