场景图,共享指针和常量传播

时间:2014-10-03 12:59:14

标签: c++ c++11 const smart-pointers

我正在研究现有的大型项目,基本上是:

  • 使用场景图,其中每个节点都有子插槽
  • 执行初始化步骤,其中每个节点递归初始化其子节点,并在其自己的数据成员上执行一些预分解/清除任务。
  • 然后开始大量计算,然后仅从const成员函数以纯只读模式访问场景图

目前,使用智能指针存储子项,主要是为了避免在从文件读取和用户编辑图形期间构建图形时进行深层复制。

由于智能指针(std :: shared_ptr)不传播constness,我有以下选项:

  1. 使用指向const对象的智能指针存储子项。什么时候 递归地执行初始化步骤, const_cast 它们到 非常量指针使用指向const的智能指针存储子节点 对象。对于递归执行初始化步骤,const_cast 它们是非常量指针。我不喜欢滥用const_cast
  2. 使用指向const对象的智能指针存储子项。什么时候 递归地执行初始化步骤,对每个子对象执行深度复制到非const对象,初始化它,并用初始化的子对象替换子对象。这是无效的,每个节点在初始化期间都被深度复制
  3. 使用指向非常量对象的智能指针存储子项。然后初始化不再是问题了,但是在计算过程中使用的所有const成员函数都可能调用子元素的非const成员函数,这是bug的潜在来源,显然不是const-correct。
  4. 我知道使用智能指针只是为了避免在树操作期间出现深拷贝,这不是在c ++ 11时代实现这一点并移动语义的好方法。用移动语义重写所有代码可能会在一天内完成,但这代表了相当大的工作。

    在您看来,实现该模式的最好方法是什么,而不用移动语义重写所有?我想过包装std :: shared_ptr来传播constness,还有其他想法吗?

    谢谢!

1 个答案:

答案 0 :(得分:0)

我会使用选项3,但将子项存储在基类或组合对象中的私有成员变量中。

仅允许通过强制执行const正确性的getter访问子节点。

const getter返回一个原始指针到const。非const getter返回原始指针或共享指针。

类似的东西:

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

template<class Child>
class Parent {
 private: 
  std::vector<std::unique_ptr<Child>> children_;
 protected:
  ~Parent() = default;
 public:
  const Child* getChild(size_t child_number) const { 
    return children_.at(child_number).get(); 
  } 
  Child* getChild(size_t child_number) {
    return children_.at(child_number).get(); 
  } 
  size_t getNumberOfChildren() const {
    return children_.size();
  }
  void addChild(std::unique_ptr<Child> child) {
    children_.emplace_back(std::move(child));
  }
};

struct Node : Parent<Node> {
 private:
  std::string name_;
 public:
  Node(std::string name) : name_(std::move(name)) {}
  void print() const { std::cout << "Node: " << name_  << "\n";}
  void setName(const std::string& name) { name_ = name; }
  void wrong() const {
    //children_[0]->setName("Wrong"); // Not allowed
    //getChild(0)->setName("Wrong"); // Not allowed
  }
};

void printRecursive(const Node* node) {
  if (node) {
    node->print();
    for (size_t i=0; i!=node->getNumberOfChildren(); ++i)
      printRecursive(node->getChild(i)); 
  }
}

int main() {
  // Initialization
  Node root("Root");
  root.addChild(std::make_unique<Node>("Child 1"));
  root.addChild(std::make_unique<Node>("Child 2"));

  // "Computation" with pointer-to-const
  const Node* root_ptr = &root;
  printRecursive(root_ptr);
}

Live demo.

Live demo - using composition.

我在我的示例中使用了unique_ptr而不是shared_ptr,因为我可以,但您可能有充分的理由使用shared_ptr