【问题标题】:Clone vector of unique pointers with RTTI使用 RTTI 克隆唯一指针向量
【发布时间】:2020-07-01 23:17:21
【问题描述】:

我想使用 RTTI 克隆一个唯一指针向量。

目前,有一个抽象基类Node 和派生类ElementTextNodeElement 包含唯一的 Node 指针向量。

我能够创建Element 类型的对象并将其移动到向量中。我希望能够克隆 Element 并将副本推送到向量中,但我正在努力使用 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();
}

实际输出:

TextNodeTextNode

预期输出:

TextNodeTextNodeTextNode

【问题讨论】:

    标签: c++ unique-ptr


    【解决方案1】:

    您所要求的不能直接使用 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();
    }
    

    但是,不用说,如果您稍后添加更多后代类,这将变得乏味且容易出错。

    更好的选择是向Node 添加一个虚拟的clone() 方法,然后让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();
    }
    

    【讨论】:

      猜你喜欢
      • 2016-06-05
      • 2014-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-10
      • 1970-01-01
      相关资源
      最近更新 更多