静态变量与静态成员

时间:2019-09-06 13:33:31

标签: c++

我在工作中看到了这个,它是我加入之前由人们写的。使用静态变量代替类的静态成员。就目前而言,我看不出为什么不应在此处使用类的静态成员的原因。如果我想说服这里的人们改变它,是否有什么好的借口说服他们?

我试图找到静态成员和静态变量之间的区别,似乎除非有充分的理由,否则人们总是倾向于使用静态成员,但没有提到非常现实的情况。

当前代码:

ListAdapter

此功能的使用方式:

class Foo {
public:

   static Foo *get() {
       static Foo _instance;

       return &_instance;
   }
// ...
};

我想到的代码应如何定义类:

int XXX_loadxxx(const char xxx, foo_handle *handle) {
  // just get foo ptr and return

  xxx::foo *ptr = xxx::foo::get();
  int ret = ptr->init();

  if (ret != 0) {
      return -1;
  }
  *handle = ptr;
  return 0;
}

如果有人能告诉我将其更改为静态成员是否有任何不同,以及原因,我将非常感谢。

4 个答案:

答案 0 :(得分:5)

第一种解决方案更好,原因有二:

  • 第一次调用get()时会初始化静态单例,这意味着您具有可预测的行为,不同翻译单元上的static变量则不是这种情况
  • 该变量是局部变量,因此仅在单例的getter中可见,这通常是您想要的

我不明白为什么您希望从方法中将其解封装。

答案 1 :(得分:2)

与静态局部变量相比,静态成员变量(以及命名空间范围内的静态变量)有一些缺点:

  • 必须在单个翻译单元中定义。对于希望成为“仅标头”的库,这不是选项。在引入内联变量的C ++ 17中,此限制不再存在。
  • 在不同转换单元中定义的静态对象的初始化顺序未定义。当静态对象依赖于其他静态对象时,这通常会导致不确定的行为,并且可能以错误的顺序进行初始化。此问题称为“静态初始化顺序失败”。函数local static(大多为 1 )解决了此问题,因为它是在首次使用时初始化的。因此,这个习惯用法称为“首次使用时构造”。

1 甚至在首次使用构造时,仍然存在一种复杂的违反顺序保证的方法:如果存在静态对象A,其构造函数不依赖于静态对象B,但是A的析构函数确实取决于B,那么B仍然可能首先被破坏,从而导致UB。如果A的任何部分依赖于B,则始终通过在A的构造函数中调用B的静态获取器可以避免这种情况。通常,由于这些问题,应避免使用静态对象。


P.S。通常,您希望从getter返回一个引用,以便调用者不必担心获取null。

P.P.S。 static Foo_instance;应该是static Foo instance;

P.P.P.S。使用静态成员时,吸气剂大部分变成 2 无效;成员可以改为公开。

2 如果您打算使用继承来扩展静态对象,同时又保持与原始接口的兼容性,那么它可能仍有一些价值。

答案 2 :(得分:2)

两个示例都无法编译。 static Foo_instance是什么意思?您是说static Foo instance吗?

现在回到您的问题:如果您在函数内部定义了静态变量,则只有在您首次调用该函数时才会对其进行初始化。有两个后果:

  • 如果您从不使用此对象,则不会浪费资源;
  • 如果构造函数需要分配其他资源,则可能需要控制创建时间。未定义静态成员的创建时间。

答案 3 :(得分:0)

在并发环境中,本地静态可变初始化是安全的。

另一个优点是,在调用相应函数的情况下,将其初始化为onlt。那就是该变量被请求初始化。

根据C ++ 17标准

  

4使用静态存储动态初始化块范围变量   持续时间(6.6.4.1)或线程存储持续时间(6.6.4.2)   控件第一次通过其声明;这样的变量   在初始化完成后被视为已初始化。   如果初始化因抛出异常而退出,则   初始化未完成,因此下一次将再次尝试   时间控制进入声明。 如果控件进入   在初始化变量的同时声明   并发执行应等待完成   。如果控件递归地重新输入声明   在初始化变量时,行为未定义