jjzjj

c++ - 我会因单例过度而感到过度杀伤力吗?

coder 2023-11-14 原文

我是C++的新手,正在编写一个跨平台(桌面/移动)2D游戏引擎...我的问题是,我是否以适当的方式使用单例,如果没有,是否有替代方法?

基本上,我的引擎中有一些组件是围绕单例对象构建的。例子:

VBOManager(单人)
这个“管理器”基本上负责分配,当然,“管理”用于存储纹理映射和顶点坐标的vbo。我通过此对象控制“读/写”,因此我可以缓存其他对象写入vbo的数据并返回指针(避免存储重复的数据,例如具有相同映射和顶点坐标的500个 Sprite )。

TextureManager(Singleton,自我解释)

GLUtils(单人)
我几乎用它来统一通用的GL调用,这些调用根据当前平台(例如GL或GLES)而有所不同。例如,GLU函数(libglu不在android上,因此我有一个自定义实现)

图形(Singleton)
用于处理诸如窗口初始化和大小调整,全局帧率管理,视口(viewport)初始化等工作。

InputManager(Singleton,自我解释)
无论如何,所以在这里我正在审查所有这些内容,并且开始感到非常肮脏。我确实认为,鉴于给定对象的要求和功能,这是合理的。但是,另一方面,我是一个新手,对于在我的代码中如此猖“的“模式”感到不对劲。如果确实是后者,那有什么替代方案?

最佳答案

在开始之前,我想弄清楚我们在说什么,以便我们都在同一页面上。单例是:

  • “对象”,这意味着它具有某些内部状态,该状态被封装为直接外部访问。它具有生命周期:它在程序执行的某个时刻创建,并在以后的某个时刻销毁。
  • “最多只能创建一个实例”,这意味着存在一些明确的机制,这些机制强制阻止创建多个实例。构造函数和析构函数在程序执行中将被调用一次。
  • “可全局访问。”一旦创建实例,就可以通过全局函数调用或类的公共(public)静态成员对其进行访问。

  • 全局对象不是单例。它只是一个全局对象。如果可以制作多个,则不是单例。

    现在,定义已不复存在:

    VBOManager (Singleton)



    我无法想象该对象如何以任何有意义的方式工作。就OpenGL而言,缓冲区对象是独立的构造。他们彼此之间没有关系。所以我不明白为什么您需要一个经理来处理所有这些问题。

    我可以理解拥有专门的包装缓冲区对象类来处理流缓冲区对象。有几种方法可以进行流传输,有些方法的性能要比其他方法更好。因此,将其包装在一个对象中是有意义的。

    但是拥有一个全局缓冲区对象管理器是没有意义的。缓冲区对象应该彼此独立。或者,如果有的话,它们应该依赖于其他对象,例如网格物体(多个网格物体可以引用相同的缓冲区)。但是,您不必具有全局缓冲区对象管理器。

    我可以理解拥有一个命名对象的存储库,可以从中添加和检索它们。但是我不理解对于像这样的特定对象类型是否需要它。使其成为全局单例的需求令人怀疑。

    我发现单例最适合根本上唯一的概念。有一个文件系统,因此拥有一个全局文件系统是有意义的。 OpenGL本身是全局的(由于其基于C的本质),尽管即使那样,也可以切换渲染上下文。如果有两个东西有意义,那么单例可能不是最好的选择。

    即使您仅使用单个缓冲区对象并为其提供管理器,也无需使此类成为单例。您可以根据需要将其设置为全局变量,但是不必强制阻止用户创建多个此类的实例。如果以后要使用多个缓冲区对象管理器(并可能要为性能付出代价),就这样吧。

    GLUtils (Singleton)



    为什么这是一个对象?单例模式指的是一个对象,在任何时候都只能有一个实例。效用函数只是全局函数。

    C++不是Java。您不必将所有内容都放在一个类中。全局功能不是坏设计。确实,在适当的情况下,它们是好的设计。如果一个函数不需要访问任何状态(除了它的参数和它可能调用的任何其他函数之外),那么就不需要它成为对象的一部分。

    Graphics (Singleton)



    对于这一点,可以认为Singleton是有意义的。它是有状态的生命物体。而且您明确希望在您的应用程序中不要多个。

    但是,请考虑这一点。拥有一个以上是很错误的吗?您将多久传递一次此Graphics对象?在Graphics对象级别需要工作多少代码?我猜不是很多。一些基本的初始化代码,仅此而已。

    因此,尽管这是有根据的,但没有必要。这不是一个必须成为Singleton的概念。另外,由于能够拥有多个,您可以随时将其删除并创建一个新的。这使您的应用程序能够更轻松地重建窗口。

    这样做的一个缺陷是可能同时具有两个Graphics对象。这涉及到处理OpenGL上下文。由于OpenGL上下文是全局状态,因此拥有多个Graphics对象可能会导致问题,因为这两个对象都有自己的上下文。您将需要某种方式将特定的Graphics对象设置为当前对象,该对象将自身绑定(bind)为当前OpenGL上下文(并检索所需的任何函数指针)。

    关于c++ - 我会因单例过度而感到过度杀伤力吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8177427/

    有关c++ - 我会因单例过度而感到过度杀伤力吗?的更多相关文章

    1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    2. ruby - 使用 `+=` 和 `send` 方法 - 2

      如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

    3. ruby - 如何计算 Liquid 中的变量 +1 - 2

      我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

    4. arrays - Ruby 数组 += vs 推送 - 2

      我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

    5. += 的 Ruby 方法 - 2

      有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

    6. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

      出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

    7. ruby-on-rails - 使用 rspec 测试自定义验证器。为什么我会得到 Proc? - 2

      如标题所示,我正在尝试使用Rspec测试自定义验证器。我得到一个错误,我不明白为什么......如果你能阐明一些问题,我将非常感激。我们开始吧:验证者规范require'spec_helper'describeGraphDateValidatordoit"shouldnotvalidateactivitywithemptystarttime"doexpect{Graph.new({start_time:''}).valid?}.toeq(false)endend如果我打印Graph.new({start_time:''}).valid?它会打印false然而,当它通过规范时,它返回一个

    8. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

      我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

    9. ruby - rails 3.2.2(或 3.2.1)+ Postgresql 9.1.3 + Ubuntu 11.10 连接错误 - 2

      我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat

    10. ruby - 在 Ruby + Chef 中检查现有目录失败 - 2

      这是我在ChefRecipe中的一blockRuby:#ifdatadirdoesn'texist,moveoverthedefaultoneif!File.exist?("/vol/postgres/data")execute"mv/var/lib/postgresql/9.1/main/vol/postgres/data"end结果是:Executingmv/var/lib/postgresql/9.1/main/vol/postgres/datamv:inter-devicemovefailed:`/var/lib/postgresql/9.1/main'to`/vol/post

    随机推荐