【问题标题】:In C++, is it possible to access a static variable in the constructor?在 C++ 中,是否可以在构造函数中访问静态变量?
【发布时间】:2011-12-03 12:03:47
【问题描述】:

我正在尝试:

class MyClass {
public:
    MyClass();

    int myID;
    static int currentID;
};

MyClass::MyClass() {
    myID = currentID;
    ++currentID;
}

我正在尝试为此类的所有实例分配一个唯一 ID。

编辑:

这对我不起作用。我在 xcode 中得到了其中两个:

未定义的符号: “GameObject::currentID”,引用自: __ZN10GameObject9currentIDE$non_lazy_ptr 在 GameObject.o (也许你的意思是:__ZN10GameObject9currentIDE$non_lazy_ptr) ld:未找到符号 collect2: ld 返回 1 个退出状态

【问题讨论】:

  • 理想情况下,您应该查看使用 boost::once 的单例,以了解静态的无竞争初始化如何工作。您还应该使用 tbb::atomic / std::atomic 来防止竞争条件。这两项修改完成,您的代码现在是线程安全的。
  • 运行时,是的。编译时,您需要在类范围之外(重新)定义int MyClass::currentID
  • @moshbear:从技术上讲,他需要定义他提供的是声明(即中的re-(重新)define 不应该在那里)
  • 如果您计划在 STL 容器中使用 MyClass,请小心。代码的行为可能与您预期的不同。
  • @DavidRodríguez-dribeas 因此括号表示不确定性。我倾向于忘记声明和定义之间的区别。

标签: c++ static constructor


【解决方案1】:

它对我有用:

#include <iostream>

class My
{
    public:
    My() : id(++currentId) {}

    int id;
    static int currentId;
};

int My::currentId = 0;

std::ostream & operator<<(std::ostream & os, My & m)
{
    return os << m.id;
}

int main()
{
    My a;
    My b;
    My c;

    std::cout << a << std::endl;
    std::cout << b << std::endl;
    std::cout << c << std::endl;
}

输出:

> ./x
1
2
3

【讨论】:

  • 没有像 vrbilgi 提到的初始化程序。你是第一个写的。为什么前面需要一个int,为什么不只是:My::currentId = 0;
  • @Ryan:那不是初始化(或者说不是only初始化),而是真正的定义静态成员:int My::currentId/* = 0*/;定义变量(为变量保留空间),删除它也初始化的注释,但重要的是这是定义
  • @stefanB:最后的评论没有意义:变量不是const
  • 是初始化静态类变量的语法
【解决方案2】:

除其他情况外,还适用于以下情况:

MyClass a[10];
MyClass* b = new MyClass[10];

如果您使用 STL 容器来保存 MyClass 对象,请务必小心。 它的行为可能与您预期的不一样,而且问题很难找到。请参见以下示例:

int MyClass::currentID = 0;
...
std::vector<MyClass> c(10);
MyClass a;

在这种情况下,结果如下:

c[0].myID = 0;
c[1].myID = 0;
....
c[9].myID = 0
=============
   a.myID = 1. 

默认构造函数只执行一次。原因是 std::vector 的构造函数将使用相同的值来初始化向量的所有元素。 在我的系统中,std::vector 的构造函数如下:

  explicit
  vector(size_type __n, const value_type& __value = value_type(),
     const allocator_type& __a = allocator_type())
  : _Base(__n, __a)
  { _M_fill_initialize(__n, __value); }   

_M_fill_initialize 将使用__value(来自默认构造函数value_type()__n 次初始化分配的内存。

上面的代码最终会调用uninitialized_fill_n,它执行以下操作:

for (; __n > 0; --__n, ++__cur)
    std::_Construct(&*__cur, __x);

这里是std::_Contruct

template<typename _T1, typename _T2>
inline void
_Construct(_T1* __p, const _T2& __value)
{
  // _GLIBCXX_RESOLVE_LIB_DEFECTS
  // 402. wrong new expression in [some_]allocator::construct
  ::new(static_cast<void*>(__p)) _T1(__value);
}

在这里你可以看到它最终调用了全局运算符new 来使用same 值初始化向量中的每个元素。

结论是,使用静态成员来初始化数据成员在大多数情况下都可以,但是如果你打算在STL容器中使用它可能会失败,并且问题很难找到。 我只试过std::vector,很有可能是在使用其他类型的带有MyClass 对象的stl 容器时存在问题。

【讨论】:

  • c++98中vector的构造函数其实就是你说的,调用默认构造函数只是为了形成默认参数。但在 C++0x 中,规范将该构造函数分为两部分:explicit vector(size_type n);vector(size_type n, const T&amp; value, const Allocator&amp; = Allocator());。对于第一个,规范说向量应该Constructs a vector with n value-initialized elements.,这需要调用默认构造函数n 次。无论如何,在这种情况下应谨慎使用OP的方法。
  • 我将 MyClass 与 stl 容器一起使用,但我只使用 stl 容器来保存指向 MyClass 的指针。例如:myVector.push_back(new MyClass());这样用可以吗?
  • @Ryan 那么没关系,只要确保每次调用默认构造函数并且您知道引擎盖下到底发生了什么。
【解决方案3】:

您收到错误的原因不是因为您从构造函数访问静态,这是合法的顺便说一句,而是因为您没有定义它。

class My
{
    public:
    My() : id(++currentId) {}

    int id;
    static int currentId;
};

//place the definition in the implementation file
int My::currentId = 0;

【讨论】:

  • @stefanB:如果你是在暗示他抄袭,我不这么认为,没有那么多事情可以做,所以他可以得到相同的代码没有 i> 复制,此外,与您的版本相反,他实际上在代码中解释,并带有注释行的作用:变量的定义。 +1 对于实际上提到缺少的部分是定义的答案
【解决方案4】:
int MyClass::currentID=0;  

如果没有完成,您需要在 CPP 文件中执行此操作

【讨论】:

    【解决方案5】:

    您需要确保在类外部定义和初始化该类的静态成员。这对我有用:

    #include <iostream>
    class MyClass {
    private:
        int myID;
        static int currentID;
    public:
       //default constructor
        MyClass()
            : myID(currentID) 
            {
                ++currentID;
            }
    
        void printId() { std::cout << "Object Id :" << myID << std::endl; } /*Prints the unique ID assigned to each object.*/
    };
    
    // define the static class member here:
    int MyClass::currentID = 1;
    
    int main()
    {
        MyClass m1, m2, m3;
        m1.printId();
        m2.printId();
        m3.printId();
    }
    

    输出:

    对象 ID:1

    对象 ID:2

    对象 ID:3

    【讨论】:

      【解决方案6】:

      可以,只要您没有 MyClass 的静态实例。

      【讨论】:

      • @Ryan 这是一个不同的问题。您需要在 cpp 文件中定义静态变量——正如 vrbilgi、StefanB 和 Luchian 现在为您演示的那样。
      【解决方案7】:

      是的,没关系。但是您编写的代码不是线程安全的:可以在不同的线程中同时实例化 2 个类。在第一个实例有时间增加静态变量之前,第二个实例可以获取相同的 currentID 值。

      这对您来说可能是也可能不是问题。

      如果您需要线程安全,this question 应该会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-10
        • 2021-07-06
        • 1970-01-01
        • 2011-09-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多