C++:为了便于访问,在类中使用静态容器来包含指向其所有对象的指针是不是一种糟糕的做法
C++ : Is it bad practice to use a static container in a class to contain pointers to all its objects for ease of access?
我想知道在类中有一个静态容器来存储指向类对象的所有指针是否是一种糟糕的做法,这样程序的基类就可以轻松地访问它们。这是一个游戏,我在sdltutorials网站上看到了它,我觉得它非常有用。这将使我的比赛有一个非常整洁的结构,我真的不认为这样做有什么缺点,但我知道我必须小心"全球"访问,也许这会产生我现在没有看到的负面影响。
以下是上下文/示例。游戏有一个基类,其中包含Loop()、Render()、PlayAudio()、CleanMemory()等基本方法。这个想法是让单个对象在基本方法中执行相同的方法。伪代码示例:
Game::Render() {
for (iterate all enemies in static container) {
current_enemy::Render();
}
}
可以肯定的是,类中的静态成员看起来是这样的:
static std::vector<Enemy*> EnemyList;
因此,通过这种方式,当你的游戏执行基础Render()方法时,例如,你可以迭代敌人类静态容器中的所有敌人,并执行他们所有的Render(
我只想确保我意识到,如果我选择这种方法来构建我的游戏,我可能会遇到任何不利因素/复杂性/限制,因为我现在看不到任何不利因素,但我知道必须小心静态和全局的东西。
非常感谢您抽出时间。
当然很方便,但是static
变量或Singleton
只不过是全局变量;具有全局变量也有缺点:
- 函数的依赖关系变得不清楚:它依赖哪个全局
- 函数的可重入性受到损害:如果
current_enemy.render()
意外调用Game::Render()
怎么办?这是一个无限递归 - 除非进行适当的同步,否则函数的线程安全性会受到损害,在这种情况下,对全局变量的串行访问会阻碍并发代码的性能(因为Amdahl定律)
在任何需要的地方显式传递对Game
实例的引用可能看起来很痛苦,也毫无意义,但它留下了一条可以遵循的依赖关系的清晰路径,随着软件的发展,您会欣赏的明确性。
当然,关于将程序转换为具有两个Game
实例,还有很多要说的。虽然在这种确切的情况下,这可能看起来不协调,但总的来说,明智的做法是不要认为这在未来永远没有必要,因为我们不是先知。
不同的人对此可能有不同的看法。我可以给你一些关于如何以更好的方式存储静态对象的建议。
对存储对象的类使用singleton模式:
class ObjectManager
{
private:
std::vector<Enemy*> enemies_;
std::vector<Friend*> friends_;
...
public:
void add(Enemy* e) { enemies_.push_back(e); }
...
const std::vector<Enemy*> enemies() const { return enmies_; }
...
private:
static ObjectManager* instance_;
public:
static ObjectManager* Get() { return instance_; }
static void Initialize() { instance_ = new ObjectManager(); }
}
你可以这样访问它(例如C++11基于范围):
void Game::Render() {
for(auto e : ObjectManager::Get()->enemies()) {
e->Render();
}
}
这对于想要访问世界信息的子类来说尤其方便。通常,您必须为每个人提供一个指向ObjectManager
的指针。但是,如果您只有一个ObjectManager
,那么单例模式可能会从代码中去除混乱。
不要忘记在程序开始时通过调用ObjectManager::Initialize();
来创建singleton。
我不建议你这样做。在这一点上,您还可以在名称空间中有一个裸露的全局变量,这与您现在正在做的事情相同。
我也不建议使用单身汉。
什么时候不应该使用Singleton模式?(除了显而易见的)
最好的方法是尽可能地进行良好的旧参数传递(依赖项注入)。经过精心设计,这在全系统范围内是可行的,它避免了全球可访问资源的所有问题。
当你没有以这种方式设计系统的奢侈,并且你在现有的代码中工作,这些代码已经有了相当多的单例依赖性问题,或者在资源之间失去了位置性(从需要它们的地方删除了几个级别)(而且你无法修改接口以向下级联依赖性),这可能不是有用的建议。
裸全局和单例之间的中间地带是服务定位器。许多人仍然认为服务定位器是一种反模式,但大多数人也认为它没有单例那么糟糕,因为它提供了一定级别的抽象,并将创建与提供对象解耦,这意味着如果您的设计或环境发生变化,您可以轻松地提供派生类。
以下是对模式的描述:
http://gameprogrammingpatterns.com/service-locator.html
下面是关于singleton与服务定位器的讨论。
如果辛格尔顿不好,那么为什么服务容器好呢?。
我最喜欢投票最高(但未被接受)的答案。
- 将包含C样式数组的对象初始化为成员变量(C++)
- 是否需要删除包含对象的"pair"?
- 如何在h文件中包含.o对象文件
- C++ 创建包含链表和字符串的对象的链接列表时出错
- 具有包含其他对象的类的对象创建顺序
- 复制包含C++所有元素的对象!(构造函数和赋值,最佳实践?
- C++ 在堆栈中包含多态属性的类对象存储
- 将 C# 对象(包含静态对象成员)作为参数传递给 C++/CLI 程序
- 如何破坏包含自指指针的对象
- 如何在构建时将 JSON 文件/对象包含在C++中
- 当对象包含其他对象的向量时,无法设置值
- 在矢量内创建一个对象,该对象由另一个对象包含
- 为什么在 C++ 中,当对象包含在另一个对象中时,复制构造函数被调用两次
- 当对象包含指针成员时,堆栈上的对象销毁出现分段错误
- 如果插槽对象包含互斥锁和条件变量,则 Boost signals2 connect() 调用编译失败
- 当包含对象包含unique_ptr时,如何删除向量的元素
- vector包含类对象,每个类对象包含3个字符串.我如何找到特定的字符串,然后删除整个元素
- 自定义对象的STL集合,每个对象包含一个STL集合
- 有多少对象(包含 std::vectors)加载到 L1/L2/L3 缓存中
- 将主应用程序对象包含在 try-catch 块中捕获所有崩溃