C++ 多态树类中的双指针向上转换(再次)、共享_ptr和泛型setChild函数
我有一个抽象的C++ 多态树类中的双指针向上转换(再次)、共享_ptr和泛型setChild函数,c++,class,subclass,shared-ptr,double-pointer,C++,Class,Subclass,Shared Ptr,Double Pointer,我有一个抽象的节点类,派生自许多子类,例如颜色,纹理,形状,灯光,等等。。。包含我的应用程序用户数据。数据包含在这些节点的大型树中。 每个节点子类都有固定数量的子类,这些子类的类型固定。例如,材质可以有一个颜色子对象和一个纹理子对象。它们存储为std::shared_ptr 目前,每个类派生一个setChild方法,该方法将节点作为参数。每个实现都会相应地使用dynamic\u cast检查类型及其子类型,并在成功时进行设置 我想在节点中实现一个通用的setChild方法,而不需要对其进行子类化
节点
类,派生自许多子类,例如颜色
,纹理
,形状
,灯光
,等等。。。包含我的应用程序用户数据。数据包含在这些节点的大型树中。
每个节点
子类都有固定数量的子类,这些子类的类型固定。例如,材质可以有一个颜色子对象和一个纹理子对象。它们存储为std::shared_ptr
目前,每个类派生一个setChild
方法,该方法将节点作为参数。每个实现都会相应地使用dynamic\u cast
检查类型及其子类型,并在成功时进行设置
我想在节点
中实现一个通用的setChild
方法,而不需要对其进行子类化。为此,每个子类将在构造函数中声明(或注册)其子类,方法是提供名称字符串、类型字符串(对应于子类)和指向共享\u ptr
节点的指针
您现在可以看到问题:
- 我将使用
**子类
,向上转换到**节点
,我知道这是错误的,但由于每个子类都有一个type()
方法来唯一标识该类,并且由于我知道每个注册的子类的类型,我可以进行双重检查,以避免使用双指针存储错误的指针类型
- 实际上,我不会使用
**节点,而是使用*std::shared\u ptr
。在这里,我不确定是否做得对
问题:
- 即使我确定类型,是否可以使用
*共享\u ptr
设置共享\u ptr
- 这是你设计的吗
谢谢
Etienne共享假定的静态指针和动态指针,实现您想要的功能。但是,考虑到您有一组固定的节点类型,我强烈推荐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());
}
...
};
类材料;
类节点;
类颜色;
类别结构;
阶级形态;
班级灯光;
班级访客
{
公众:
虚拟无效访问(const shared_ptr&inNode)=0;
虚拟无效访问(const shared_ptr&inNode)=0;
虚拟无效访问(const shared_ptr&inNode)=0;
虚拟无效访问(const shared_ptr&inNode)=0;
虚拟无效访问(const shared_ptr&inLight)=0;
}
类节点
{
公众:
虚拟无效接受(访问者和访问者)=0;
};
类颜色
:公共节点
,public boost::从\u中启用\u共享\u
{
公众:
虚拟无效接受(访问者和访问者)
{
inVisitor.visit(从这个()共享);
}
...
};
类纹理
:公共节点
,public boost::从\u中启用\u共享\u
{
公众:
虚拟无效接受(访问者和访问者)
{
inVisitor.visit(从这个()共享);
}
...
};
阶级形态
:公共节点
,public boost::从\u中启用\u共享\u
{
公众:
虚拟无效接受(访问者和访问者)
{
inVisitor.visit(从这个()共享);
}
...
};
班灯
:公共节点
,public boost::从\u中启用\u共享\u
{
公众:
虚拟无效接受(访问者和访问者)
{
inVisitor.visit(从这个()共享);
}
...
};
整个模式的目的是做一些类似于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);
}
...
};
class SetMaterialChild
:公众访客
{
公众:
SetMaterialChild(材料与非材料)
:m材料(inMaterial)
{ }
虚拟无效访问(const shared_ptr&inNode)
{
mMaterial.mColor=inNode;
}
虚拟无效访问(const shared_ptr&inNode)
{
mMaterial.mTexture=inNode;
}
虚拟无效访问(const shared_ptr&inNode)
{
抛出std::runtime_错误(“材质不能有形状”);
}
虚拟无效访问(const shared_ptr&inLight)
{
抛出std::runtime_错误(“材质不能有灯光”);
}
私人:
材料和材料)
};
课堂材料
:公共节点
,public boost::从\u中启用\u共享\u
{
公众:
虚拟无效接受(访问者和访问者)
{
inVisitor.visit(从这个()共享);
}
void setNode(const shared_ptr&inNode)
{
SetMaterialChild v(*本);
inNode->accept(v);
}
...
};
最初,这种访问者模式很难实现。但是,它在场景图中非常流行,因为它非常擅长处理一小部分节点类型,而且它是解决问题的最安全的方法。如果我理解您的意思,以下情况是不安全的:
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*>
boost::shared_ptr realPtr;
boost::shared_ptr*castpr;
//castPtr=&realPtr;——无效,无法从一个强制转换到另一个
castPtr=重新解释铸件(&castPtr);
castPtr->reset(任何内容);//非法的。castPtr不指向
//到boost::shared\u ptr
<>现在你可能会走运,记忆可能会排成一行,但那不是有效的C++。
如果您希望扩展第三方插件中的节点集,onl
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;
};