我正在进行一个现有的大型项目,基本上是:
- 使用场景图,其中每个节点都有子老虎机
- 执行初始化步骤,其中每个节点递归地初始化其子节点,并对其自己的数据成员执行一些预计算/清理任务
- 然后开始大规模计算,然后仅从const成员函数以纯只读模式访问场景图
目前,使用智能指针存储子对象,主要是为了在从文件读取构建图时以及在用户编辑图时避免深度复制。
由于智能指针(std::shared_ptr)不会传播常量,因此我有以下选项:
- 使用指向常量对象的智能指针存储子对象。什么时候递归地执行初始化步骤,const_cast它们非常数指针使用指向常量的智能指针存储子项对象。对于递归执行初始化步骤,const_cast它们指向非常数指针。我不喜欢滥用const_cast
- 使用指向常量对象的智能指针存储子对象。什么时候递归地执行初始化步骤,对每个子对象执行深度复制到一个非常量对象,初始化它,并用初始化的子对象替换子对象。这不是有效的,每个节点在初始化期间都被深度复制
- 使用指向非常量对象的智能指针存储子对象。那么初始化就不再是问题了,但计算过程中使用的所有常量成员函数都可能调用子级的非常量成员函数,这是潜在的错误源,显然是非常量正确的
我知道,在c++11和移动语义时代,只使用智能指针来避免树操作期间的深度复制并不是实现这一点的好方法。用move语义重写所有代码可能有一天就能完成,但这是一项相当大的工作。
在您看来,在不使用移动语义重写所有内容的情况下,实现该模式的最佳方式是什么?我想过包装std::shared_ptr来传播constness,还有其他想法吗?
谢谢!
我会选择选项3,但将子级存储在基类或组合对象的私有成员变量中。
只允许通过强制常量正确性的getter访问子级。
const getter返回一个指向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);
}
现场演示。
现场演示-使用合成。
在我的示例中,我使用了unique_ptr
而不是shared_ptr
,因为我可以,但您可能有充分的理由使用shared_ptr
。