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;
};