c++ - 如何在不使用 'friend' 的情况下允许另一个类创建但允许继承?

标签 c++ inheritance components friend

我的游戏引擎总共有三个类,一个所有组件都继承自它的抽象 IComponent 类,一个组件类(在本例中我将使用 RenderComponent)和一个 ComponentManager。我希望 ComponentManager 类能够使用 RenderComponent 的构造函数,但我不希望任何其他类创建 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 的子类型的创建者需要注册一种方法来构造子类型的实例。他们向可以构造该类实例的工厂注册一个函数或一个类。

示例程序

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

关于c++ - 如何在不使用 'friend' 的情况下允许另一个类创建但允许继承?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43105798/

相关文章:

javascript - 使用javascript加载knockoutjs组件

javascript - Vue动态添加不同组件

c++ - 在循环中异步使用 ReadDirectoryChangesW

python - 创建从它继承的对象时在 Python 中有条件地导入

c++ - If 语句:值列表

c++ - C++中的类接口(interface)继承

c++ - 虚继承构造函数顺序

delphi - 由于 '%CLASSGROUP TPersistent' ,为什么工具选项板中可用于数据模块的组件比可用于表单的组件少?

C++:模板问题 (C2064)

c++ - 如何将 CLion (1.2.4) 用于涉及 Qt Creator 的项目?