如何在不使用 'friend' 的情况下允许另一个人创建 class 但允许继承?

How to allow a class to be created by another without using 'friend' but allow inheritence?

我的游戏引擎总共有三个 class,一个抽象的 IComponent class,其中所有组件都继承自它,一个组件 class(对于这个例子,我将使用 RenderComponent) 和一个 ComponentManager。我希望 ComponentManager class 能够使用 RenderComponent 的构造函数,但我不希望任何其他 classes 创建 RenderComponent 的实例,但我不想使用 'friend' 因为我希望客户用户组件继承自 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();

}

重申一下,我希望用户创建组件的工作量最小。另一种选择当然是为两者都提供构造函数 public 但是有了它,虽然你可以在任何你想创建的地方创建一个组件,但它是无用的。

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

IComponent 的子类型的创建者需要注册一种方法来构建子类型的实例。他们向可以构造 class.

实例的工厂注册一个函数或 class

示例程序

#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.

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

IComponent* component = new RenderComponent();

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

所以我只是选择拥有所有东西 public,只是让随机创建的组件变得无用,在我的引擎文档中我将描述你必须在中使用 GameObject::AddComponent()为了创建和使用组件。

谢谢。