树结构中的运行时元素子状态 我正在实现C++中的REST API客户端,它需要能够在运行时发现API的结构。RESTAPI的结构也可能在程序执行期间发生变化。API模型基于json数组和对象。如果返回数组,则表示检索到的元素是节点;如果返回对象,则表示检索到的元素是叶

树结构中的运行时元素子状态 我正在实现C++中的REST API客户端,它需要能够在运行时发现API的结构。RESTAPI的结构也可能在程序执行期间发生变化。API模型基于json数组和对象。如果返回数组,则表示检索到的元素是节点;如果返回对象,则表示检索到的元素是叶,c++,rest,oop,design-patterns,C++,Rest,Oop,Design Patterns,为了表示我正在建模的数据,我决定使用复合模式为树结构节点或叶的用户提供一个公共接口 为了向某些GUI或CLI提供模型的挂钩,我决定使用访问者模式来访问节点和叶子 我还尝试使用访问者模式实现模型的发现部分,但它显然不适合该任务,因为我需要将以前分配的派生类型的对象替换为另一种类型,例如:叶成为节点。在我看来,使用访问者模式似乎是不可能的,因为对象无法替换自身 我试图添加一些容器类来包装我的对象,但我最终遇到了类似的情况,我需要一个对象来替换自己 我在网上阅读了有关设计模式的文章,试图找到一种可以帮

为了表示我正在建模的数据,我决定使用复合模式为树结构节点或叶的用户提供一个公共接口

为了向某些GUI或CLI提供模型的挂钩,我决定使用访问者模式来访问节点和叶子

我还尝试使用访问者模式实现模型的发现部分,但它显然不适合该任务,因为我需要将以前分配的派生类型的对象替换为另一种类型,例如:叶成为节点。在我看来,使用访问者模式似乎是不可能的,因为对象无法替换自身

我试图添加一些容器类来包装我的对象,但我最终遇到了类似的情况,我需要一个对象来替换自己

我在网上阅读了有关设计模式的文章,试图找到一种可以帮助我解决问题的模式,但我还没有找到任何合适的模式

以下是我到目前为止的代码-最相关的部分是在最终类Discover:

#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()....
  }

};
是否有任何方法可以在运行时替换以前分配的对象,而不必向创建的对象添加指针数组并在需要时更改它们


谢谢

对于您在提供的代码中分配的所有元素,您可以通过调用new动态地进行分配

对于所有可能被调用的discover元素,这也是真的吗

如果是这样的话,在调用的根的某处,将是该元素的真正所有者。例如,如何更改discover的接口,使其返回指向新元素的指针,沿着调用链一直返回到旧元素的真正所有者?然后,所有者可以删除旧元素,并开始将指向新分配元素的指针存储在其位置


,当然,通常不要使用原始指针,使用STD::UnjyQupTR或STD::SyrdY-PTR,或者其他合适的方法,但是上面的代码转换也是可能的。

没有替换C++中现有对象的简单方法。C++不这样工作。远程实现这一点的唯一方法是手动调用现有对象的析构函数,然后使用placement new,并确保提前分配足够的内存以容纳最大的替换对象。如果您不知道这意味着什么,这可能意味着您将无法根据这是当今使用的最复杂的通用编程语言的所有规则正确地实现它。另一个选择可能是C++的std::variant。建议:制作一个能隔离您的确切问题的示例,并就此提出一个问题。您现在提供的代码是1%的物质,99%的噪音。即使你坚持重点,你也会有更高的机会让别人理解你的问题。@SamVarshavchik我唯一能想到的替换现有对象的方法就是你所描述的。我是一名嵌入式c程序员和固件设计师,所以手动管理内存对我来说不是问题。问题是,在我看来,这在很大程度上是一种c的做事方式。。。如果我找不到其他解决方案,我会退回到这个解决方案。我将看一看std::variant,看看它是否可以帮助我。谢谢你好我刚刚根据您的建议测试了这个方法,添加了一个ReturningVisitor接口,该接口带有返回元素*的访问函数,并使用这个接口作为派生类Discover的基础。还向我的元素类添加了必需的accept方法。它似乎工作得很好。我只测试了将ElementEmpty转换为ElementLeaf,因为为ElementComposite添加必要的代码将更具挑战性,但它看起来确实可以工作。我会回来让你知道它是否有效,并接受你的答案。谢谢因此,在替换deque中的对象时,我会有一些memleaks,但我会解决它。其他一切似乎都很好!再次感谢!伟大的如前所述,如果您开始使用std::unique_ptr或std::shared_ptr而不是原始指针,那么就更容易避免意外引入内存泄漏。我现在正在研究unique_ptr,它似乎适合我的需要。在我的实现中,我必须做的唯一一个小改变是让discover函数只返回json对象,而不是元素*,因为我必须根据正在发生的转换做出不同的反应,例如:清空CompositeElement vs完全没有更改vs等等。。。。很接近测试 在实时API上下载它!: