【问题标题】:Defining and accessing objects in a large framework在大型框架中定义和访问对象
【发布时间】:2014-12-08 05:22:24
【问题描述】:

在需要实现的大型框架中定义包装类对象(main 之外)的好方法是什么,它们可以从任何地方访问。

(例如,在 Player.cpp 等游戏对象中使用 Clock.Get_Deltatime() 并在 main.cpp 中运行 Clock.Update())

我可以这样做吗?

#ifndef PLUGIN_H
#define PLUGIN_H

#include "DisplayManager.h"
#include "EventHandler.h"
#include "Time.h"

DisplayManager Display;
EventHandler Input;
Time Clock;

#endif PLUGIN_H

在我需要访问显示、时钟和输入的任何时候都包括 Plugin.h 吗?考虑到这一点,现在单例和静态变量,但我正在寻找建议并想知道什么最适合这种情况(我对 C++ 比较陌生,没有足够的 OOP 经验来知道什么是最好的工具工作)。

【问题讨论】:

    标签: c++ oop design-patterns singleton


    【解决方案1】:

    上面的代码可以工作,但您需要使用extern 关键字声明变量,这样编译器就不会在每次包含标头时生成新的全局变量。在您的应用程序中的一处声明不带 extern 关键字的全局变量,这将是实际初始化变量的地方。

    我建议使用以下模式,而不是上述模式。我将以 DisplayManager 为例:

    DisplayManager.h:

    #ifndef DISPLAY_MANAGER_H_
    #define DISPLAY_MANAGER_H_
    
    class DisplayManager {
     public:
      static DisplayManager* Get();
    
      // Any other public interfaces.
    
     private:
      // Declare the constructor private or protected to prevent instances other
      // than the singleton.
      DisplayManager();
      ~DisplayManager();
    
      // Any other private functions or members.
    };
    

    DisplayManager.cpp:

    #include "DisplayManager.h"
    
    static DisplayManager* g_display_manager;
    
    DisplayManager* DisplayManager::Get() {
      if (!g_display_manager) {
         g_display_manager = new DisplayManager();
      }
      return g_display_manager;
    }
    
    ...
    

    这种方法将单例行为封装在类本身中。

    【讨论】:

    • 我可以看到extern 关键字是多么乏味,但是包含防护不会阻止实例的多个实例吗?我想我可以通过使用静态变量对它们进行计数并多次包含plugins.h 来测试它。仍在研究单身人士是否是一个好方法。我认为他们会工作,但他们似乎真的不喜欢。 stackoverflow.com/questions/86582/…
    • 包含防护将防止编译器因同一变量的多个声明而失败。但是,包含此头文件的每个源文件都将拥有类的 OWN 实例。通过使用 extern,编译器知道不创建对象的新实例,并将让链接器解决该问题。然后,您需要在非头文件中实例化对象的实例。
    • 如果您正确使用单件并出于正确的原因,它们是完全可以接受的。人们之所以不喜欢它们,只是因为它们可以像全局变量一样被滥用。
    • 哦,我明白了。我想我会选择单身人士。谢谢。
    【解决方案2】:

    此答案使用 C++11

    在单线程环境中,单例的以下模式效果很好:

    #include <memory>
    
    class DisplayManager
    {
    public:
      static DisplayManager* instance()
      {
        static std::unique_ptr<DisplayManager> ptr(new DisplayManager);
        return ptr.get();
      }
    private:
      friend struct std::default_delete<DisplayManager>;
      DisplayManager() {}
      ~DisplayManager() {}
      DisplayManager(const DisplayManager& rhs) {}
      DisplayManager& operator= (const DisplayManager& rhs) { return *this; }
    };
    

    如果需要保证多线程的正确访问,改成:

    #include <memory>
    #include <mutex>
    
    class DisplayManager
    {
    public:
      static DisplayManager* instance()
      {
        static std::unique_ptr<DisplayManager> ptr;
        static std::mutex m;
        if (!ptr)
        {
          m.lock();
          if (!ptr) ptr.reset(new DisplayManager);
          m.unlock();
        }
        return ptr.get();
      }
    private:
      friend struct std::default_delete<DisplayManager>;
      DisplayManager() {}
      ~DisplayManager() {}
      DisplayManager(const DisplayManager& rhs) {}
      DisplayManager& operator= (const DisplayManager& rhs) { return *this; }
    };
    

    【讨论】:

    • 目前使用 MS Visual Studio 2012 Express Edition。这显然使用了 199711。不幸的是,据我所知,它的版本是 C++ 98。我写的2D游戏引擎不使用多线程。
    • 如果你不需要多线程,你仍然可以使用上面的代码并进行一些调整。主要是把unique_ptr换成auto_ptr,把朋友声明改成匹配。
    猜你喜欢
    • 2016-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 2019-03-02
    • 2020-05-09
    相关资源
    最近更新 更多