将静态变量声明为函数/成员函数是不好的做法?

Pap*_*ter 10 c++ static

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

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

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

    init = true;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

谢谢!

Mik*_*son 8

这当然是罕见的,至少在高质量的代码中,因为它适合的狭窄情况.这基本上做的是全球状态的即时初始化(以提供一些全局功能).一个典型的例子是具有随机数生成器功能,该功能在第一次调用时为发生器播种.另一个典型用法是返回在第一次调用时初始化的单例实例的函数.但其他用例的例子很少.

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

与全局数据一样,多线程会像这样简单实现一些问题.对全局状态的天真介绍永远不会具有本质上的线程安全性,并且这种情况也不例外,您必须采取措施来解决该特定问题.这也是全球各州不可取的原因之一.

可以使用非静态成员变量实现static bool init到SomeClass :: function的行为.

如果有替代方案可以实现相同的行为,那么必须根据技术问题(如线程安全性)判断两种替代方案.但在这种情况下,所需的行为是可疑的,比实现细节更多,并且替代实现的存在不会改变它.

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

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

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

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

OOPish与否,谁在乎呢?但是,全球国家是这里的关注点.与其说使用本地静态变量来实现其初始化.全球各州,特别是可变的全球国家,总的来说很糟糕,绝不应该被滥用.他们阻碍模块(模块是自给自足的,如果他们依靠全局状态更少),它们引入了多线程的担忧,因为他们本身是共享数据,他们让使用它们的非重入(非纯)的任何功能,它们使调试很难等...这个清单还在继续.但是大多数这些问题与您实施它的方式无关.在另一方面,使用局部静态变量是解决静态初始化阶惨败的好方法,所以,他们是很好的原因,少了一个问题担心引入(完全合理的)全局状态时进入你的代码.


NPE*_*NPE 5

想想多线程。function()当可以由多个线程同时调用时,这种类型的代码是有问题的。如果没有锁定,您就可以接受竞争条件;使用锁定时,并发性可能会受到影响而没有任何实际收益。

  • @AndyProwl:我不是那个意思。我的意思是两个线程可以愉快地进入“if (!init)”块。 (2认同)
  • @AndyProwl 初始化,是的,但是里面还有 `if (!init)` 。 (2认同)