17

最近一位同事向我展示了这样的代码:

void SomeClass::function()
{
    static bool init = false;

    if (!init)
    {
        // hundreds of lines of ugly code
    }

    init = true;
}

他想检查是否SomeClass已初始化,以便每个Someclass实例执行一次代码,但事实是SomeClass在程序的整个生命周期中只存在一个实例。

他的问题是关于init静态变量,关于它的初始化时间。我已经回答说初始化发生一次,因此该值将false在第一次调用和true其生命周期的其余时间。回答后,我补充说这样使用静态变量是不好的做法,但我无法解释原因。

到目前为止,我一直在考虑的原因如下:

  • static bool initinto的行为SomeClass::function可以通过非静态成员变量来实现。
  • 中的其他函数SomeClass无法检查该static bool init值,因为它的可见性仅限于void SomeClass::function()范围。
  • 静态变量不是OOPish,因为它们定义了全局状态而不是对象状态。

这个原因对我来说看起来很糟糕,不聪明而且不是很具体,所以我要求更多的理由来解释为什么在函数和成员函数空间中使用静态变量是一种不好的做法。

谢谢!

4

4 回答 4

13

这肯定是一种罕见的情况,至少在高质量的代码中是这样,因为它适用于狭窄的情况。这基本上是对全局状态进行即时初始化(以提供一些全局功能)。一个典型的例子是有一个随机数生成器函数,它在第一次调用它时为生成器提供种子。另一个典型的用法是返回单例实例的函数,在第一次调用时初始化。但是其他用例示例很少而且相差甚远。

一般而言,全局状态是不可取的,并且具有包含自给自足状态的对象是优选的(对于模块化等)。但是如果你需要全局状态(有时你需要),你必须以某种方式实现它。如果您需要任何类型的非平凡全局状态,那么您可能应该使用单例类,并且提供该应用程序范围的单个实例的首选方法之一是通过提供对已初始化的本地静态实例的引用的函数在第一次通话。如果需要的全局状态稍微简单一点,那么使用本地静态 bool 标志执行该方案肯定是一种可接受的方式。换句话说,我认为采用这种方法没有根本问题,但如果出现这样的代码,我自然会质疑它的动机(需要全局状态)。

与全局数据的情况一样,多线程会导致像这样的简单实现出现一些问题。全局状态的天真引入永远不会本质上是线程安全的,这种情况也不例外,您必须采取措施解决该特定问题。这也是全球国家不可取的部分原因。

静态 bool init 进入 SomeClass::function 的行为可以通过非静态成员变量来实现。

如果有替代方案可以实现相同的行为,则必须根据技术问题(如线程安全)来判断这两个替代方案。但是在这种情况下,所需的行为是有问题的,而不是实现细节,替代实现的存在并没有改变这一点。

其次,我看不出如何用基于非静态数据成员(也许是静态数据成员)的任何东西来替换全局状态的即时初始化。即使可以,这也是一种浪费(需要为每个程序执行一次的东西存储每个对象),并且仅在这一点上,不会使它成为更好的选择。

SomeClass 中的其他函数无法检查静态 bool 初始化值,因为它的可见性仅限于 void SomeClass::function() 范围。

我通常会将其放在“Pro”列中(如 Pro/Con 中)。这是一件好事。这是信息隐藏或封装。如果你能隐藏不应该让别人关心的事情,那就太好了!但是如果有其他函数需要知道全局状态是否已经初始化,那么你可能需要更多类似于单例类的东西。

静态变量不是 OOPish,因为它们定义了全局状态而不是对象状态。

OOPish 与否,谁在乎?但是,是的,全球状态是这里的关注点。与其说是使用局部静态变量来实现其初始化。全局状态,尤其是可变的全局状态,总体上是不好的,不应该被滥用。它们阻碍了模块化(如果模块依赖于全局状态,它们的自给自足性会降低),它们引入了多线程问题,因为它们本质上是共享数据,它们使任何使用它们的函数都不可重入(非纯),它们进行调试困难,等等......名单还在继续。但是这些问题中的大多数与您如何实现它无关。另一方面,使用局部静态变量是解决静态初始化顺序失败的好方法,因此,它们是好的,因此在引入(合理的)全局状态时要担心的问题更少进入你的代码。

于 2013-03-06T17:37:42.830 回答
5

考虑多线程。function()当可以由多个线程同时调用时,这种类型的代码是有问题的。没有锁定,你就可以接受竞争条件;使用锁定,并发可能没有真正的收益。

于 2013-03-06T16:32:27.187 回答
2

全局状态可能是这里最严重的问题。其他功能不必关心它,所以这不是问题。它可以在没有static变量的情况下实现这一事实本质上意味着您制作了某种形式的单例。这当然会引入单例的所有问题,例如完全不适合多线程环境。

于 2013-03-06T16:33:44.287 回答
2

添加到其他人所说的,您不能同时拥有此类的多个对象,或者至少它们不会按预期运行。第一个实例将设置静态变量并进行初始化。后来创建的那些虽然没有自己的版本,init但与所有其他实例共享。由于第一个实例将其设置为 true,所有后续都不会进行任何初始化,这很可能不是您想要的。

于 2013-03-06T16:44:11.353 回答