【发布时间】:2020-02-16 16:13:30
【问题描述】:
我正在用 C++ 实现一个 REST API 客户端,它需要能够在运行时发现 API 的结构。 REST API 的结构也可能在程序执行期间发生变化。 API 模型基于 json 数组和对象。如果返回一个数组,则表示检索到的元素是一个节点,如果返回一个对象,则表示检索到的元素是一个叶子。
为了表示我正在建模的数据,我决定使用复合模式为用户提供树结构(节点或叶)的通用接口。
为了给我的模型提供一些 GUI 或 CLI 的钩子,我决定使用访问者模式来访问节点和叶子。
我也尝试使用访问者模式来实现模型的发现部分,但它显然不适合该任务,因为我需要将以前分配的派生类型的对象替换为另一种类型(例如:叶子变成节点)。在我看来,使用访问者模式是不可能的,因为对象不能替换自己。
我尝试添加一些容器类来包装我的对象,但我最终遇到了类似的情况,我需要一个对象来替换自己。
我在网上阅读了有关设计模式的信息,试图找到一种可以帮助我的方法,但我没有找到任何合适的。
这是我到目前为止的代码 - 最相关的部分在最后(发现类):
#include <string>
#include <deque>
#include <stack>
#include <iostream>
class Visitor;
class Element{
public:
Element(std::string name, Element* parent) : name{ name }, parent{ parent }{}
std::string get_name(){
return name;
}
std::string get_url(){
std::stack<std::string> url_stack;
Element* ancestor = parent;
url_stack.push(name);
while(ancestor != NULL){
url_stack.push(ancestor->name);
ancestor = ancestor->parent;
}
std::string url;
while(!url_stack.empty()){
url.append(url_stack.top());
url_stack.pop();
}
return url;
}
Element* get_parent(void){
return parent;
}
virtual ~Element(void){};
virtual void accept(Visitor& visitor) = 0;
private:
std::string name;
Element* parent;
};
class ElementEmpty : public Element{
public:
ElementEmpty(std::string name, Element* parent = NULL) : Element(name, parent){}
virtual void accept(Visitor& visitor);
};
class ElementLeaf : public Element{
public:
ElementLeaf(std::string name, std::string value, Element* parent = NULL) : Element(name, parent), value{ value }{}
virtual void accept(Visitor& visitor);
std::string value;
};
class ElementComposite : public Element{
public:
ElementComposite(std::string name, std::deque<std::string> value_names, Element* parent = NULL) : Element(name, parent){
for(auto& vn : value_names){
values.emplace_back(new ElementEmpty(vn, this));
}
}
~ElementComposite(){
while(!values.empty()){
delete values.back();
values.pop_back();
}
}
virtual void accept(Visitor& visitor);
std::deque<Element*> values;
};
class Visitor{
public:
virtual void visit(ElementEmpty& empty) = 0;
virtual void visit(ElementLeaf& leaf) = 0;
virtual void visit(ElementComposite& composite) = 0;
};
void ElementEmpty::accept(Visitor& visitor){
visitor.visit(*this);
}
void ElementLeaf::accept(Visitor& visitor){
visitor.visit(*this);
}
void ElementComposite::accept(Visitor& visitor){
visitor.visit(*this);
}
class Printer : public Visitor{
public:
virtual void visit(ElementEmpty& empty){
std::cout << empty.get_name() << " is empty" << std::endl;
}
virtual void visit(ElementLeaf& leaf){
std::cout << leaf.get_name() << " is a leaf with value" << leaf.value << std::endl;
}
virtual void visit(ElementComposite& composite){
std::cout << composite.get_name() << " is composite, visiting childrens" << std::endl;
for(auto& v : composite.values){
v->accept(*this);
}
}
};
class Discover : public Visitor{
public:
virtual void visit(ElementEmpty& empty){
discover(&empty);
}
virtual void visit(ElementLeaf& leaf){
discover(&leaf);
}
virtual void visit(ElementComposite& composite){
discover(&composite);
for(auto& v : composite.values){
v->accept(*this);
}
}
private:
void discover(Element* element){
std::string url = element->get_url();
//HTTP GET (pseudo-code here to avoid adding irelevant code)
json_val value = http_get(url);
Element* new_element;
if(value.is_array()){
new_element = new ElementComposite(element->get_name(), value.as_array().as_deque(), element->get_parent());
}
else if(value.is_object()){
new_element = new ElementLeaf(element->get_name(), value.as_string(), element->get_parent());
}
else{
new_element = new ElementEmpty(element->get_name(), element->get_parent());
}
//TODO: How to set element to new_element? it's impossible since we are currently in element->accept()....
}
};
有什么方法可以在运行时替换以前分配的对象,而不必添加指向已创建对象的指针数组并在需要时更改它们?
谢谢!
【问题讨论】:
-
在 C++ 中“替换”现有对象没有简单的方法。 C++ 不能以这种方式工作。唯一可行的方法是手动调用现有对象的析构函数,然后使用放置
new,并确保提前分配足够的内存以容纳最大可能的“替换”对象。如果您不知道这意味着什么,则可能意味着您将无法根据 this 的所有规则正确实现它,这是当今使用的最复杂的通用编程语言。另一个选择可能是 C++ 的std::variant。 -
建议:生成一个minimal-reproducible-example 来隔离您的确切问题并提出相关问题。您现在提供的代码是 1% 物质,99% 噪音。即使你坚持重点,别人也更有可能理解你的问题。
-
@SamVarshavchik 我能想到的替换现有对象的唯一方法正是您所描述的。我是嵌入式 c 程序员和固件设计师,因此手动管理内存对我来说不是问题。问题是在我看来,这在很大程度上是一种 c 的做事方式......如果我找不到其他任何东西,我会回退到这个解决方案。我会看看 std::variant 看看它是否可以帮助我。谢谢!
标签: c++ rest oop design-patterns