多态树类中的双指针upcast(再次)、shared_ptr和泛型setChild函数
Double pointer upcast (again), shared_ptr and generic setChild function in a polymorphic tree class
我有一个抽象的Node
类,派生自许多子类,如Color
、Texture
、Shape
、Light
等……其中包含我的应用程序用户数据。数据包含在这些节点的大型树中。每个Node
子类都有固定数量的子类,这些子类是固定类型的。例如,"材质"可以有一个子"颜色"和一个子"纹理"。这些存储为std::shared_ptr
的
目前,每个类都派生一个setChild
方法,该方法以Node
作为参数。每个实现使用dynamic_cast
相应地检查类型及其子类型,如果成功,则进行设置。
我想在Node
中实现一个通用的setChild
方法,而不需要对其进行子类化。为此,每个子类都会在构造函数中声明(或注册)其子类,方法是给出一个名称字符串、一个类型字符串(对应于子类)和一个指向shared_ptr
到Node
的指针。
你现在可以看到问题了:
- 我将使用
**SubClass
,向上转换为**Node
,我知道这很糟糕,但由于每个子类都有一个type()
方法来唯一标识类,并且对于每个注册的子类,我知道它的类型,所以我可以仔细检查,以避免用双指针存储错误的指针类型 - 事实上,我不会用
**Node
,而是用*std::shared_ptr<Node>
来做这件事。在这里,我不确定是否做对了什么
问题:
- 即使我确定类型,也可以用
*shared_ptr<Node>
设置shared_ptr<Subclass>
吗 - 这是你设计的方式吗
谢谢,
Etienne
如果我理解你的意思,以下是不安全的:
boost::shared_ptr<SpecificNode> realPtr;
boost::shared_ptr<Node>* castPtr;
// castPtr = &realPtr; -- invalid, cannot cast from one to the other
castPtr = reinterpret_cast<boost::shared_ptr<Node>*>(&castPtr);
castPtr->reset(anything); // illegal. castPtr does not point
// to a boost::shared_ptr<Node*>
现在你可能很幸运,内存可能会排成一行,但这不是有效的C++。
如果你想在第三方插件中扩展节点集,唯一的解决方案是一系列动态强制转换,所以让我们看看我们可以做些什么来实现注册。
与其尝试使用强制转换执行所有操作,不如考虑使用模板执行类型安全操作。有一个abtral基类,它接受到节点的共享ptr,并且要么使用它,要么不使用它
(从现在开始,我将使用T::Ptr而不是boost::shared_Ptr,假设typedef在那里。这只是为了stackoverflow易于阅读)
class Registration
{
public:
typedef boost::shared_ptr<Registration> Ptr;
virtual bool consume(const Node::Ptr&) = 0;
};
template <typename T>
class SpecificRegistration : public Registration
{
public:
SpecificRegistration(T::Ptr& inChildPtr)
: mChildPtr(inChildPtr)
{ }
virtual bool consume(const Node:Ptr& inNewValue)
{
if(!inNewValue) {
mChildPtr.reset();
return true; // consumed null ptr
} else {
T::Ptr newValue = dynamic_pointer_cast<T>(inNewValue);
if (newValue) {
mChildPtr = newValue;
return true; // consumed new value
} else {
return false; // no match
}
}
}
private:
T::Ptr& mChildPtr;
};
template <typename T>
Registration::Ptr registerChild(T::Ptr& inChildPtr)
{
return make_shared<SpecificRegistration<T> >(inChildPtr);
}
// you can also register vector<T::Ptr> if you write an
// ArraySpecificRegistration class which uses push_back when
// it consumes a node
void Node::setNode(const Node& inNode) {
for (RegistrationList::iterator iter = mRegistration.begin(); iter != mRegistration.end(); ++iter) {
if (mRegistration->consume(inNode))
return;
}
throw runtime_error("Failed to set any children of the node");
}
class SomeThirdPartyNode
: public Node
{
public:
SomeThirdPartyNode()
{
// all they have to write is one line, and mOtherTHing is
// registered
addChild(registerChild(mOtherThing));
}
private:
SomeOtherThirdPartyNode::Ptr mOtherThing;
};
shared_pr假定static_pointer_cast和dynamic_pointer_cast。但是,考虑到您有一组固定的节点类型,我强烈推荐访问者模式。考虑到您似乎在做场景图,我更倾向于推荐它,因为Visitor的最佳用法是使用场景图。
class Material;
class Node;
class Color;
class Texture;
class Shape;
class Light;
class Visitor
{
public:
virtual void visit(const shared_ptr<Material>& inNode) = 0;
virtual void visit(const shared_ptr<Color>& inNode) = 0;
virtual void visit(const shared_ptr<Texture>& inNode) = 0;
virtual void visit(const shared_ptr<Shape>& inNode) = 0;
virtual void visit(const shared_ptr<Light>& inLight) = 0;
}
class Node
{
public:
virtual void accept(Visitor& inVisitor) = 0;
};
class Color
: public Node
, public boost::enable_shared_from_this<Color>
{
public:
virtual void accept(Visitor& inVisitor)
{
inVisitor.visit(shared_from_this());
}
...
};
class Texture
: public Node
, public boost::enable_shared_from_this<Texture>
{
public:
virtual void accept(Visitor& inVisitor)
{
inVisitor.visit(shared_from_this());
}
...
};
class Shape
: public Node
, public boost::enable_shared_from_this<Shape>
{
public:
virtual void accept(Visitor& inVisitor)
{
inVisitor.visit(shared_from_this());
}
...
};
class Light
: public Node
, public boost::enable_shared_from_this<Light>
{
public:
virtual void accept(Visitor& inVisitor)
{
inVisitor.visit(shared_from_this());
}
...
};
整个模式的目的是做一些类似于dynamic_cast的事情,只是它是通过一对虚拟函数调用而不是任意的dynamic_cast来完成的。Node::accept负责调用一个知道对象确切类型的函数(即Light::accept知道"this"是Light*)。然后,它用"正确"的类型信息调用访问者的访问函数。
该系统被设计为支持许多在一小组类型上操作的算法。例如,您的setChild函数
class SetMaterialChild
: public Visitor
{
public:
SetMaterialChild(Material& inMaterial)
: mMaterial(inMaterial)
{ }
virtual void visit(const shared_ptr<Color>& inNode)
{
mMaterial.mColor = inNode;
}
virtual void visit(const shared_ptr<Texture>& inNode)
{
mMaterial.mTexture = inNode;
}
virtual void visit(const shared_ptr<Shape>& inNode)
{
throw std::runtime_error("Materials cannot have shapes");
}
virtual void visit(const shared_ptr<Light>& inLight)
{
throw std::runtime_error("Materials cannot have lights");
}
private:
Material& mMaterial)
};
class Material
: public Node
, public boost::enable_shared_from_this<Material>
{
public:
virtual void accept(Visitor& inVisitor)
{
inVisitor.visit(shared_from_this());
}
void setNode(const shared_ptr<Node>& inNode)
{
SetMaterialChild v(*this);
inNode->accept(v);
}
...
};
这种访问者模式最初很难接近。然而,它在场景图中非常受欢迎,因为它非常擅长处理一小组节点类型,并且是解决问题的最安全的方法
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- dopen():不以 root 身份运行时"failed to map segment from shared object"
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 无法使用 libtool 将 -shared 参数传递给 g++
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- shared-ptr-C++shared_ptr与unique_ptr用于资源管理