【问题标题】:How to allow a class to be created by another without using 'friend' but allow inheritence?如何允许一个类在不使用“朋友”的情况下由另一个类创建但允许继承?
【发布时间】:2017-08-23 15:59:38
【问题描述】:

我的游戏引擎总共有三个类,一个抽象的 IComponent 类,其中所有组件都从它继承,一个组件类(在本示例中,我将使用 RenderComponent)和一个 ComponentManager。我希望 ComponentManager 类能够使用 RenderComponent 的构造函数,但我不希望任何其他类创建 RenderComponent 的实例,但我不想使用“朋友”,因为我希望客户用户组件继承来自 IComponent 并自动在 ComponentManager 中使用,而不允许实例化自己的。代码示例模糊地显示了我想要发生的行为:

class GameObject;

class IComponent
{
    private:
        IComponent() { }
        ~IComponent() { }
    public:
        GameObject* Parent;
}

class RenderComponent : IComponent
{
    public:
        RenderComponent() { }
        ~RenderComponent() { }
}

class ComponentManager
{
    public:
        ComponentManager() { }
        ~ComponentManager() { }

        // normally this would be a template function, but for the sake of this example I will directly use RenderComponent
        RenderComponent* CreateComponent()
        {
            // this would not throw a compiler error
            return new RenderComponent();
        }
}

int main()
{
    ComponentManager manager;

    // even though the constructor of RenderComponent is public, this would throw an error
    RenderComponent* render = new RenderComponent();

    // this however would work perfectly fine
    RenderComponent* render = manager.CreateComponent();

}

重申一下,我希望它可以让用户在创建组件时付出最少的努力。当然,另一种选择是让两者的构造函数都公开,但拥有它,这样虽然你可以在任何你想要的地方创建一个组件,但它是无用的。

【问题讨论】:

  • 您可以使用工厂模式来做到这一点,但不是您尝试的方式。
  • 虽然我不知道在制作工厂模式时如何不使用“朋友”,但如果它解决了我的问题,我会切换到它。
  • 您对为什么不想使用friend 的解释没有真正的意义。你能详细说明一下吗?
  • 我只是不希望用户每次制作自己的组件时都必须执行friend class ComponentManager。我可以只公开构造函数/析构函数,但我也想限制用户实例化组件的能力。

标签: c++ inheritance components friend


【解决方案1】:

首先我要感谢R Sahu 提供他的答案,但在他的示例中仍然允许用户做我一开始想避免的事情,随机能够创建一个组件的实例:

IComponent* component = new RenderComponent();

现在我意识到我想要的是不可能的,如果不限制对 ComponentManager 的访问,就无法限制用户对构造函数/析构函数的访问。因此,要么我的实现是错误的或不完整的(这不太可能但可能),要么我要求的是不合逻辑的东西(最有可能的答案)。

因此,我只是选择公开所有内容,只是让随机创建的组件无用,在我的引擎文档中,我将描述您必须使用 GameObject::AddComponent() 才能创建和使用组件。

谢谢。

【讨论】:

    【解决方案2】:

    如果你使用工厂设计模式,ComponentManager 不需要知道IComponent 的具体子类型。无需将其声明为子类型的朋友。它可以简单地使用一个工厂来构造对象。

    IComponent 子类型的创建者将需要注册一种方法来构造子类型的实例。他们向可以构造类实例的工厂注册一个函数或一个类。

    一个示例程序

    #include <iostream>
    #include <map>
    #include <string>
    
    class GameObject;
    
    class IComponent
    {
       // Make sure that sub-classes of IComponent can use the constructor
       // and the destructor.
       protected:
          IComponent() { }
          ~IComponent() { }
    
       public:
          GameObject* Parent;
    };
    
    // Define a function type that can construct a Component.
    using ComponentConstructor = IComponent*  (*)();
    
    // Define the interface for the factory.
    class ComponentFactory
    {
       public:
    
          // A type alias for simpler coding.
          using ConstructorMap = std::map<std::string, ComponentConstructor>;
    
          // Allow creators of sub-classes of IComponent to register a
          // function that can be used to construct the sub-type.
          static void registerComponentConstructor(std::string const& componentType,
                                                   ComponentConstructor constructor);
    
          // Construct a Component by providing a name corresponding
          // to the derived sub-type of IComponent.
          static IComponent* constructComponent(std::string const& componentType);
    
       private:
    
          // Private function that maintains a map of
          // constructors.
          static ConstructorMap& getConstructrMap();
    };
    
    // -----------------------------------------------------
    // BEGIN implementation of ComponentFactory.
    // It can, obviously, be in a .cpp file of its own.
    void ComponentFactory::registerComponentConstructor(std::string const& componentType,
                                                        ComponentConstructor constructor)
    {
       getConstructrMap()[componentType] = constructor;
    }
    
    IComponent* ComponentFactory::constructComponent(std::string const& componentType)
    {
       ConstructorMap& constructorMap = getConstructrMap();
       ConstructorMap::iterator iter = constructorMap.find(componentType);
       if ( iter != constructorMap.end() )
       {
          return iter->second();
       }
       else
       {
          return nullptr;
       }
    }
    
    ComponentFactory::ConstructorMap& ComponentFactory::getConstructrMap()
    {
       static ConstructorMap theMap;
       return theMap;
    }
    
    // END implementation of ComponentFactory.
    // -----------------------------------------------------
    
    // ComponentManager can use ComponentFactory to 
    // construct Components.
    class ComponentManager
    {
       public:
          ComponentManager() { }
          ~ComponentManager() { }
    
          IComponent* CreateComponent(std::string const& componentType)
          {
             return ComponentFactory::constructComponent(componentType);
          }
    };
    
    // Test code.
    // Construct IComponents by using appropriate names.
    int main()
    {
       ComponentManager m;
       IComponent* ic1 = m.CreateComponent("RenderComponent");
       if ( ic1 == nullptr )
       {
          std::cout << "Unable to construct a Component of type RenderComponent.\n";
       }
       else
       {
          std::cout << "Successfully constructed a Component of type RenderComponent.\n";
       }
    
       IComponent* ic2 = m.CreateComponent("AnotherTypeOfComponent");
       if ( ic2 == nullptr )
       {
          std::cout << "Unable to construct a Component of type AnotherTypeOfComponent.\n";
       }
       else
       {
          std::cout << "Successfully constructed a Component of type AnotherTypeOfComponent.\n";
       }
    
       IComponent* ic3 = m.CreateComponent("FooComponent");
       if ( ic3 == nullptr )
       {
          std::cout << "Unable to construct a Component of type FooComponent.\n";
       }
       else
       {
          std::cout << "Successfully constructed a Component of type FooComponent.\n";
       }
    }
    
    // Client components.
    // Without these, no Component can be constructed.
    
    namespace Module1
    {
       class RenderComponent : IComponent
       {
          public:
             RenderComponent() { }
             ~RenderComponent() { }
    
             static IComponent* constructComponent()
             {
                return new RenderComponent();
             }
    
             struct Initer
             {
                Initer()
                {
                   ComponentFactory::registerComponentConstructor("RenderComponent",
                                                                  RenderComponent::constructComponent);
                }
             };
       };
    
       // The constructor makes sure that
       // RenderComponent::constructComponent() is
       // registered as the function to be called to
       // construct objects of type RenderComponent when
       // the name "RenderComponent" is used.
       // 
       // A different method may be used for the purpose but
       // this seems like a straight forward method to do that.
       static RenderComponent::Initer initer;
    
    }
    
    namespace Module2
    {
       class AnotherTypeOfComponent : IComponent
       {
          public:
             AnotherTypeOfComponent() { }
             ~AnotherTypeOfComponent() { }
    
             static IComponent* constructComponent()
             {
                return new AnotherTypeOfComponent();
             }
    
             struct Initer
             {
                Initer()
                {
                   ComponentFactory::registerComponentConstructor("AnotherTypeOfComponent",
                                                                  AnotherTypeOfComponent::constructComponent);
                }
             };
       };
    
       // The constructor makes sure that
       // AnotherTypeOfComponent::constructComponent() is
       // registered as the function to be called to
       // construct objects of type AnotherTypeOfComponent when
       // the name "AnotherTypeOfComponent" is used.
       static AnotherTypeOfComponent::Initer initer;
    }
    

    输出

    Successfully constructed a Component of type RenderComponent.
    Successfully constructed a Component of type AnotherTypeOfComponent.
    Unable to construct a Component of type FooComponent.
    

    【讨论】:

    • 我会用我的代码测试一下,看看它是否有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-19
    • 2015-07-02
    • 2020-03-06
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    相关资源
    最近更新 更多