【问题标题】:c++ call constructor of extended class stored in base class variable without knowing extended classc ++调用存储在基类变量中的扩展类的构造函数而不知道扩展类
【发布时间】:2017-03-08 21:14:01
【问题描述】:

我目前正在编写一些遇到此问题的程序:

我有一个Scene 类和一个MainScene 类:

class Scene{
    Scene();
}

class MainScene : public Scene{
    MainScene();
}

我想要做的是跟踪这样的场景列表:

std::map<std::string, Scene*> scenes;

然后我像这样向它添加一个场景:

MainScene* mainscene; //I do not make it a new because i want my scenemanager to handle that
scenes.emplace("mainscene", mainscene); // add it to the list

我有这样的功能:

template <class T>
void SceneManager::recreateScene(T* &scene)
{
    scene = new T();
}

这样当我想使用函数loadscene时,我可以从列表中抓取场景并删除当前场景并使用函数recreateScene创建新场景。但是地图给了我Scene。所以当我使用recreateScene 时,它调用Scene() 的构造函数而不是MainScene()。但我需要它知道列表中的场景是MainScene,因此它会创建new MainScene() 而不是new Scene()

【问题讨论】:

  • 听起来你需要在你的场景类中有一个虚函数,并在每个“扩展”类中覆盖它。
  • 混合模板和运行时多态性总是很困难的。而多态的目的是“忘记”特殊类型模板需要它们。

标签: c++ class templates pointers extending-classes


【解决方案1】:

一种方法是将创建者存储在指针旁边。像这样的:

std::map<std::string, std::pair<Scene*, std::function<Scene*()>> scenes;

scenes.emplace("mainscene", {nullptr, []() { return new MainScene(); }});

然后,修改recreateScene

template <class T>
void SceneManager::recreateScene(Scene* &scene, std::function<Scene*()> creator)
{
  scene = creator();
}

// called as:
auto& s = scenes["mainscene"];
recreateScene(s.first, s.second);

旁注:如果这些指针拥有Scene 对象,它们不应该是原始指针,而是std::unique_ptr&lt;Scene&gt;。 DTTO 为std::function 的返回类型。

【讨论】:

    【解决方案2】:

    满足您需求的一种简单有效的方法是通过以下方式实现“self-rebuild”功能:

    class Scene {
        virtual ~Scene();
        virtual void self_rebuild() = 0;
    };
    
    class MainScene : public Scene {
        ~MainScene();
        void self_rebuild() {
            this->~MainScene();    // destroy scene (without deallocation)
            new(this) MainScene(); // rebuild a MainScene object in place (without reallocation)
        }
    };
    

    此方法的主要优点是无需释放/重新分配即可清理/重建对象。这是完全安全的,只要该对象是类MainScene 的最派生对象。

    参考标准(3.8/7):

    如果在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,在原对象占用的存储位置创建一个新对象,一个指向原对象的指针,引用原始对象的引用或原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,可用于操作新对象,如果:

    • 新对象的存储完全覆盖存储 原始对象占据的位置,以及
    • 新对象 与原始对象的类型相同(忽略顶层 cv 限定符),以及
    • 原始对象的类型不是 const 限定,并且,如果是类类型,则不包含任何 类型为 const 限定或引用的非静态数据成员 输入,然后
    • 原始对象是最衍生的对象 (1.8) 类型 T 并且新对象是类型 T 的最派生对象(即 是,它们不是基类子对象)。

    【讨论】:

      【解决方案3】:

      场景上的纯虚函数如何,称为“克隆”,它返回一个场景*? MainScene 然后可以有类似Scene* clone() {return new MainScene(*this);}

      【讨论】:

      • 这仅适用于首先要克隆的Scene*
      • "void SceneManager::recreateScene(T* &amp;scene)" 问题状态有
      • Scene* scene = nullptr; recreateScene(scene) 在问题中也可能有效。
      • 当然——尽管这个概念会破坏任何设计。这就像说“我想克隆一个类但不知道什么类”
      • 这就是发生的事情......将未初始化的MainScene 指针存储在Scene 指针的映射中。我并不是说您的解决方案不好。但是根据我在问题中看到的信息,这可能有一些限制。
      【解决方案4】:

      我建议让对象在虚函数中处理它们自己的创建。这样你就不需要跟踪类型了。

      class Scene{
          Scene();
          virtual ~Scene();
      
          virtual Scene* ReCreate() = 0;
      }
      
      class MainScene : public Scene{
          MainScene();
          virtual ~MainScene();
      
          virtual Scene *ReCreate(){
              return new MainScene();
          }
      }
      
      template <class T>
      void SceneManager::recreateScene(T* &scene){
          Scene *temp = scene;
          scene = scene->ReCreate();
          delete temp;
      }
      

      【讨论】:

        猜你喜欢
        • 2014-04-18
        • 2014-05-29
        • 1970-01-01
        • 2021-02-13
        • 2018-09-26
        • 2013-11-15
        • 1970-01-01
        • 2013-05-29
        • 2011-07-28
        相关资源
        最近更新 更多