我应该使用全局变量吗?

发布于 2024-11-24 12:39:00 字数 2132 浏览 2 评论 0原文

我一直在阅读有关全局变量以及它们有多糟糕的内容,但因此我被困在一个地方。我将非常具体地说明在这种情况下是否应该使用全局变量。

我正在开发游戏引擎。我的引擎由很多经理组成。管理器执行某些任务 - 他们存储资源、加载资源、更新资源等。

我已将所有管理器设置为单例,因为有很多类和函数需要访问它们。我正在考虑删除单例,但我不知道如何才能拥有它并访问这些管理器。

这是我想要讲述的示例(抱歉,我英语不好):

Singleton.h

template<class T> class Singleton {
private:
    Singleton( const Singleton& );
    const Singleton& operator=( const Singleton& );

protected:
    Singleton() { instance = static_cast<T*>(this); }
    virtual ~Singleton() {}

protected:
    static T * instance;

public:
    static T &Instance() {
        return *instance;
    }

};

ScriptManager.h

class ScriptManager : public Singleton<ScriptManager> {
public:
    virtual void runLine(const String &line)=0;
    virtual void runFile(const String &file)=0;
};

PythonScriptManager.cpp

class PythonScriptManager : public ScriptManager {
public:
    PythonScriptManager() { Py_Initialize(); }
    ~PythonScriptManager() { Py_Finalize(); }

    void runFile(const String &file) {
        FILE * fp = fopen(file.c_str(), "r");
        PyRun_SimpleFile(fp, file.c_str());
        fclose(fp);
        fp=0;
    }

    void runLine(const String &line) {
        PyRun_SimpleString(line.c_str());   
    }

};

Entity ScriptComponent

#include <CoreIncludes.h>
#include <ScriptManager.h>
#include <ScriptComponent.h>

void update() {

    ScriptManager::Instance().runFile("test_script.script");
    //i know its not a good idea to open the stream on every frame but thats not the main concern right now.
}

Application

int main(int argc, const char * argv) {
    Application * app = new Application(argc, argv);
    ScriptManager * script_manager = new PythonScriptManager;
    //all other managers

    return app->run();
}

正如您所见,我什至没有将上面的文件包含在我的 ScriptComponent.cpp 文件中为我赢得了一些编译时间。如果没有全局变量,我怎样才能得到这样的结果,这将使它很容易像这样集成。单例不是线程安全的,但是添加线程不会花费很长时间。

我希望我能解释这个问题。

预先感谢,
卡西姆·加西姆扎达

I have been reading about global variables and how bad they are but I am stuck in one place due to that. I am going to be very specific about if I should use global variables in this scenario.

I am working on a game engine. And my engine consists of lots of managers. Managers do certain tasks - they store resources, load them, update them etc.

I have made all my managers a singleton because so many classes and functions needs access to them. I was thinking of removing the singleton but I don't know how i can not have it and get access to these managers.

Here is an example of what I am trying to tell (im bad at english, sorry):

Singleton.h

template<class T> class Singleton {
private:
    Singleton( const Singleton& );
    const Singleton& operator=( const Singleton& );

protected:
    Singleton() { instance = static_cast<T*>(this); }
    virtual ~Singleton() {}

protected:
    static T * instance;

public:
    static T &Instance() {
        return *instance;
    }

};

ScriptManager.h

class ScriptManager : public Singleton<ScriptManager> {
public:
    virtual void runLine(const String &line)=0;
    virtual void runFile(const String &file)=0;
};

PythonScriptManager.cpp

class PythonScriptManager : public ScriptManager {
public:
    PythonScriptManager() { Py_Initialize(); }
    ~PythonScriptManager() { Py_Finalize(); }

    void runFile(const String &file) {
        FILE * fp = fopen(file.c_str(), "r");
        PyRun_SimpleFile(fp, file.c_str());
        fclose(fp);
        fp=0;
    }

    void runLine(const String &line) {
        PyRun_SimpleString(line.c_str());   
    }

};

Entity ScriptComponent

#include <CoreIncludes.h>
#include <ScriptManager.h>
#include <ScriptComponent.h>

void update() {

    ScriptManager::Instance().runFile("test_script.script");
    //i know its not a good idea to open the stream on every frame but thats not the main concern right now.
}

Application

int main(int argc, const char * argv) {
    Application * app = new Application(argc, argv);
    ScriptManager * script_manager = new PythonScriptManager;
    //all other managers

    return app->run();
}

As you see I am not even including the files above in my ScriptComponent.cpp file which wins me some compilation time. How can I get that kind of a result without globals which will make it easy to integrate as this one. The singleton is not thread safe but adding threads won't take a long time.

I hope I could explain the problem.

Thanks in advance,
Gasim Gasimzada

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

海拔太高太耀眼 2024-12-01 12:39:00

我不会说你永远不应该使用全局变量,但是:

  • 永远不要使用单例。 这里< /a> 就是原因。它们很可怕,而且比普通的旧全局变量要糟糕得多。
  • “经理”课程很糟糕。他们“管理”什么?他们如何“管理”它? “经理”类需要分解为您可以描述的内容。一旦您弄清楚“管理”对象的含义,您就可以定义一个或多个具有更明确职责的对象。
  • 当您使用全局变量时,不要使它们可变。只写全局变量是可以接受的(考虑一个记录器。你写入它,但它的状态永远不会影响应用程序),只读全局变量也可以(考虑各种从未改变的常量,但你经常使用它们)需要阅读)。全局变量变得有害的地方是当它们具有可变状态时:当您对它们进行读取和写入时。

最后,非常非常简单的替代方案:
只需将依赖项作为参数传递即可。如果一个对象需要某些东西才能运行,请在其构造函数中传递该“东西”。如果函数需要某些东西才能运行,请将“某些东西”作为参数传递给它。

这听起来可能需要大量工作,但事实并非如此。当你的设计中充斥着全局变量和单例变量时,你会得到一个巨大的意大利面条式架构,其中一切都依赖于其他一切。因为依赖关系不是明确可见的,所以你会变得草率,而不是思考连接两个组件的最佳方式是什么,你只是让它们通过一个或多个全局变量进行通信。一旦您必须考虑显式传递哪些依赖项,大多数依赖项都会被证明是不必要的,并且您的设计会变得更加清晰、更具可读性和可维护性,并且更容易推理。并且您的依赖项数量将急剧下降,因此您实际上只需要向少量对象或函数传递一个或两个额外的参数。

I won't say you should never use globals, but:

  • Never use singletons. Here is why. They're horrible, and they're much worse than plain old globals.
  • "Manager" classes are bad. What do they "manage"? How do they "manage" it? "Manager" classes need to be broken up into something that you can describe. Once you've figured out what it means to "manage" an object, you can define one or more object with better-defined responsibilities.
  • When you use globals, don't make them mutable. A write-only global can be acceptable (consider a logger. You write to it, but its state never ever affects the application), and read-only globals can be ok too (consider various constants that are never changed, but which you frequently need to read from). Where globals become harmful is when they have mutable state: when you both read to, and write from, them.

And finally, the very very simple alternative:
Just pass dependencies as arguments. If an object needs something in order to function, pass it that "something" in its constructor. If a function needs something in order to operate, pass it that "something" as an argument.

This might sound like a lot of work, but it isn't. When your design is cluttered with globals and singletons, you get a big huge spaghetti architecture where everything depends on everything else. Because the dependencies are not explicitly visible, you get sloppy, and rather than thinking about what the best way to connect two components is, you just make them communicate through one or more globals. Once you have to think about which dependencies to explicitly pass around, most of them turn out to be unnecessary, and your design becomes much cleaner, more readable and maintainable, and much much easier to reason about. And your number of dependencies will drop dramatically, so that you only actually need to pass an extra argument or two to a small number of objects or functions.

心头的小情儿 2024-12-01 12:39:00

删除 ScriptManager 基类并在专业化类中使用静态方法怎么样?看起来任何 ScriptManager 都没有涉及任何状态,除了纯虚函数之外也没有真正的继承。

我无法从你的代码示例中弄清楚你是否真的在这里使用了多态性。如果没有,静态成员函数对我来说看起来不错。

How about removing the ScriptManager base class and use static methods in the specialization classes? It looks like there is no state involved with any ScriptManagers, and no real heritage other than the purely virtual functions.

I could not figure out from your code samples if you actually use polymorphism here. If not, static member functions look OK to me.

日久见人心 2024-12-01 12:39:00

永远不要使用全局变量。如果您需要某种类型的对象,则可以在必要时通过引用传递它。

Do not ever use global variables. If you need an object of a type, then you pass it in, by reference if necessary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文