我想使用RTTI克隆唯一指针的 vector 。
当前,有一个抽象基类Node
以及派生类Element
和TextNode
。 Element
包含一个唯一的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
方法,然后让Element
和TextNode
(以及将来的后代)覆盖它来制作自己的副本,例如:#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/