All derived-classes 的显式实例化

Explicit instantiation for All derived-classes

我有一个 Component class:

class Component 
{

}

然后继承自它的Colliderclass:

class Collider :public Component
{

}

还有一个 SphereCollider 继承自 Collider:

class SphereCollider :public Collider
{

}

最后,一个 GameObject class,您可以使用它添加或删除组件。

GameObject.hheader:

class GameObject
{
    //Add functionality
    template <class T>
    T& AddComponent();

    //Get functionality
    template <typename T>
    T& GetComponent();
}

GameObject.cpp来源:

template <typename T>
T& GameObject::AddComponent(){}

template <typename T>
T& GameObject::GetComponent(){}

//Explicit instantiation 
template Component &GameObject::GetComponent<Component>();
template Component &GameObject::AddComponent<Component>();
template SphereCollider &GameObject::GetComponent<SphereCollider>();
template SphereCollider &GameObject::AddComponent<SphereCollider>();

一切正常。我可以在没有指针的情况下从 GameObject 添加或获取组件,如下所示:

GameObject gameObject;
SphereCollider sCollider1 = gameObject.AddComponent<SphereCollider>();
SphereCollider sCollider2 = gameObject.GetComponent<SphereCollider>();
Component sCollider3 = gameObject.GetComponent<Component>();

问题:

我需要将其用作库并隐藏源文件。由于我在 GameObject.cpp 源代码中为 ComponentSphereCollider classes 添加了显式实例化,因此只有那些可以编译。

例如,

Collider sCollider = gameObject.GetComponent<InnoEngine::Collider>();

将无法编译,除非我还添加:

template Collider &GameObject::GetComponent<Collider>();
template Collider &GameObject::AddComponent<Collider>();

GameObject.cpp 来源中。

由于所有使用GetComponentAddComponent函数的东西都继承自Componentclass,有没有办法自动进行显式实例化对于从 Component class?

继承的每个 class

我试过了template Component &GameObject::GetComponent<Component>();template Component &GameObject::AddComponent<Component>(); 但这没有用,只给了我 "unresolved external symbol" 错误。

目前我唯一的选择是将所有模板函数放在 header 文件中,但这将公开 GetComponentAddComponent 模板函数的重要代码.

不,这不可能。

1) template Component &GameObject::GetComponent<Component>() 是仅针对具体 Component class 的显式实例化。编译器找不到纯虚拟方法的定义,因此您会收到链接器错误。

2) 如果您不添加相应的包含,某些从 Component 派生的 class 可能无法在 GameObject.cpp 中访问。所以在一般情况下,编译器甚至在理论上也无法获得所有继承者的列表,因为此信息仅在链接时可用。

Since everything that uses the GetComponent and AddComponent functions inherits from the Component class, is there a way to automatically make Explicit instantiation for every class that inherits from the Component class?

简短的回答是 "No"。

Component 派生出 class 就可以做到这一点。您得到一个派生的 class —— 仅此而已。如果基 class 具有 virtual 成员函数,这些成员函数在派生 class 中被覆盖,您将获得在派生 class.

中实现的行为

任何额外的东西都必须编码。需要编写的额外代码量取决于额外功能和您提供的框架。

这是实现您的目标的一种可能方法。请注意这里使用了dynamic_cast,一不小心可能会失败

class Component 
{
   public:
      virtual ~Component() {}
};

#include <map>

// Implement GameObject with no knowledge of any of the classes derived 
// from Component. That will be one sure fire way of making sure that it can
// be used for any class derived from Component.

class GameObject
{
   public:

    //Add functionality
    template <class T>
    T& AddComponent()
    {
       T* obj = new T{};
       AddComponent(getTypeID<T>(), obj);
       return *obj;
    }

    //Get functionality
    template <typename T>
    T& GetComponent()
    {
       T* obj = dynamic_cast<T*>(GetComponent(getTypeID<T>()));
       return *obj;
    }

   private:

    // If necessary, the implementation can be moved to a .cpp file.
    void AddComponent(int typeID, Component* obj)
    {
       components[typeID] = obj;
    }

    // If necessary, the implementation can be moved to a .cpp file.
    Component* GetComponent(int typeID)
    {
       return components[typeID];
    }

    // Key function.
    // It gets a unique integer for every type.
    template <class T>
       static int getTypeID()
       {
          static int typeID = getNextID();
          return typeID;
       }

    // If necessary, the implementation can be moved to a .cpp file.
    static int getNextID()
    {
       static int nextID = 0;
       return ++nextID;
    }

    std::map<int, Component*> components;
};

class Collider : public Component
{
};

class SphereCollider :public Collider
{
};

int main()
{
   GameObject go;
   go.AddComponent<Collider>();
   go.AddComponent<SphereCollider>();

   Collider c = go.GetComponent<Collider>();
   SphereCollider sc = go.GetComponent<SphereCollider>();
}