C++中的静态构造函数?我需要初始化私有静态对象

Gordon Gustafson 172 c++ static private static-constructor initializer

我想要一个具有私有静态数据成员的类(包含所有字符az的向量).在java或C#中,我可以创建一个"静态构造函数",它将在我创建类的任何实例之前运行,并设置类的静态数据成员.它只运行一次(因为变量是只读的,只需要设置一次),因为它是类的一个函数,它可以访问它的私有成员.我可以在构造函数中添加代码来检查向量是否已初始化,如果不是,则初始化它,但是这会引入许多必要的检查,并且似乎不是问题的最佳解决方案.

我想到,因为变量只是只读的,所以它们只能是公共的静态const,所以我可以在类外面设置它们,但是再一次,它看起来有点像丑陋的黑客.

如果我不想在实例构造函数中初始化它们,是否可以在类中拥有私有静态数据成员?

Daniel Earwi.. 178

要获得静态构造函数的等价物,您需要编写一个单独的普通类来保存静态数据,然后创建该普通类的静态实例.

class StaticStuff
{
     std::vector<char> letters_;

public:
     StaticStuff()
     {
         for (char c = 'a'; c <= 'z'; c++)
             letters_.push_back(c);
     }

     // provide some way to get at letters_
};

class Elsewhere
{
    static StaticStuff staticStuff; // constructor runs once, single instance

};

  • 是.我总是向人们指出,如果C++没有犯下所有这些"错误",那么其他语言就必须制造它们.C++涵盖了如此多的基础,甚至犯了错误,对于其后的语言来说非常棒. (108认同)
  • @Oleg:是的,他们这样做.标准保证所有非局部变量的构造函数在输入main之前执行.它还保证在编译单元内,构造顺序定义明确,并且与编译单元内的声明顺序相同.不幸的是,他们没有在多个编译单元中定义顺序. (13认同)
  • 这实际上是`friend`很有意义的情况,因此类'Elsewhere`可以轻松访问`StaticStuff`的内部结构(不会以任何危险的方式破坏封装,我可能会添加). (13认同)
  • 谢谢!虽然这样做非常烦人.C#和java从中学到的许多"错误"之一. (12认同)
  • 只是一个小细微差别,当构造函数发挥作用时,没有人保证静态对象的构造函数执行时.一个众所周知的更安全的方法是类Elsewhere {StaticStuff&get_staticStuff(){static StaticStuff staticStuff; //构造函数运行一次,当有人第一次需要它时返回staticStuff; }}; 我想知道C#和Java中的静态构造函数是否可以提供与上面代码相​​同的保证... (10认同)
  • 至于语言战争,Java有一些很好的东西,C++没有,因为它们相隔数十年创建,然后C#在Java之后大约五年出现,这应该不足为奇.这肯定没有争议.此外,C++和其他两种语言甚至不能达到同样的目的. (2认同)

EFraim.. 80

好吧,你可以拥有

class MyClass
{
    public:
        static vector<char> a;

        static class _init
        {
          public:
            _init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
        } _initializer;
};

不要忘记(在.cpp中)这个:

vector<char> MyClass::a;
MyClass::_init MyClass::_initializer;

程序仍将在没有第二行的情况下链接,但初始化程序不会被执行.

  • @ur.:`_initializer`是`MyClass`的子对象.子对象按此顺序初始化:虚拟基类子对象,按深度优先,从左到右的顺序(但仅初始化每个不同的子对象一次); 然后是普通的基类子对象,按深度优先,从左到右的顺序; 然后成员子对象按声明顺序排列.因此,只要`_initialiser`中的代码仅引用在其之前声明的成员,使用EFraim的策略是安全的. (4认同)
  • 你好,我在哪里可以找到更多关于这个"初始化"魔术? (2认同)
  • 这个答案比公认的要好,因为作者提到了第二个代码片段上必不可少的初始化。 (2认同)

emkey08.. 24

C++ 11解决方案

由于C++ 11,你可以用lambda表达式直接初始化静态类成员.您不再需要使用任何变通办法.

头文件:

class MyClass {
    static const vector<char> letters;
    static const size_t letterCount;
};

源文件:

// Initialize MyClass::letters by using a lambda expression.
const vector<char> MyClass::letters = [] {
    vector<char> letters;
    for (char c = 'a'; c <= 'z'; c++)
        letters.push_back(c);
    return letters;
}();

// The initialization order of static members is defined by the order of
// definition within the source file, so we can access MyClass::letters here.
const size_t MyClass::letterCount = letters.size();

此模式是静态构造函数的完全替代:

  • 您对所有静态成员都有一个定义良好的初始化顺序:只是与在文件中定义的顺序相同.
  • 您可以在lambda表达式中读取和写入其他(已初始化的!)静态成员.
  • 您可以初始化依赖于其他静态成员的静态成员.您只需按正确的顺序初始化它们.

  • 静态程序初始化代码必须**绝不要抛出任何异常,否则程序将崩溃。如果可能引发异常,则必须将初始化程序逻辑包装到“ try catch”块中。 (3认同)

Ant.. 19

在.h文件中:

class MyClass {
private:
    static int myValue;
};

在.cpp文件中:

#include "myclass.h"

int MyClass::myValue = 0;

  • 这适用于单个静态成员(无论何种类型).与静态构造函数相比的缺点是您不能在各种静态成员之间强加*order*.如果您需要这样做,请参阅Earwicker的答案. (5认同)

小智.. 14

这是另一种类似于Daniel Earwicker的方法,也使用了Konrad Rudolph的朋友类建议.这里我们使用内部私有友好实用程序类来初始化主类的静态成员.例如:

头文件:

class ToBeInitialized
{
    // Inner friend utility class to initialize whatever you need

    class Initializer
    {
    public:
        Initializer();
    };

    friend class Initializer;

    // Static member variables of ToBeInitialized class

    static const int numberOfFloats;
    static float *theFloats;

    // Static instance of Initializer
    //   When this is created, its constructor initializes
    //   the ToBeInitialized class' static variables

    static Initializer initializer;
};

实施文件:

// Normal static scalar initializer
const int ToBeInitialized::numberOfFloats = 17;

// Constructor of Initializer class.
//    Here is where you can initialize any static members
//    of the enclosing ToBeInitialized class since this inner
//    class is a friend of it.

ToBeInitialized::Initializer::Initializer()
{
    ToBeInitialized::theFloats =
        (float *)malloc(ToBeInitialized::numberOfFloats * sizeof(float));

    for (int i = 0; i < ToBeInitialized::numberOfFloats; ++i)
        ToBeInitialized::theFloats[i] = calculateSomeFancyValue(i);
}

这种方法的优点是可以将Initializer类完全隐藏在外部世界中,从而保持类中包含的所有内容的初始化.


bitwise.. 11

Test::StaticTest() 在全局静态初始化期间只调用一次.

调用者只需要向要作为其静态构造函数的函数添加一行.

static_constructor<&Test::StaticTest>::c;c在全局静态初始化期间强制初始化.

template<void(*ctor)()>
struct static_constructor
{
    struct constructor { constructor() { ctor(); } };
    static constructor c;
};

template<void(*ctor)()>
typename static_constructor<ctor>::constructor static_constructor<ctor>::c;

/////////////////////////////

struct Test
{
    static int number;

    static void StaticTest()
    {
        static_constructor<&Test::StaticTest>::c;

        number = 123;
        cout << "static ctor" << endl;
    }
};

int Test::number;

int main(int argc, char *argv[])
{
    cout << Test::number << endl;
    return 0;
}


Marc Mutz - .. 9

无需init()功能,std::vector可以从一个范围创建:

// h file:
class MyClass {
    static std::vector<char> alphabet;
// ...
};

// cpp file:
#include <boost/range.hpp>
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
std::vector<char> MyClass::alphabet( boost::begin( ::alphabet ), boost::end( ::alphabet ) );

但请注意,类类型的静态会导致库中出现问题,因此应避免使用它们.

C++ 11更新

从C++ 11开始,您可以这样做:

// cpp file:
std::vector<char> MyClass::alphabet = { 'a', 'b', 'c', ..., 'z' };

它在语义上等同于原始答案中的C++ 98解决方案,但是您不能在右侧使用字符串文字,因此它并不完全优越.但是,如果有任何其它类型的比的向量char,wchar_t,char16_tchar32_t(数组其可以写为字符串)时,C++ 11版本将严格除去样板代码而不引入其它语法,相比于C++ 98版.


Martin York.. 6

静态构造函数的概念是在他们从C++中的问题中学习之后在Java中引入的.所以我们没有直接的等价物.

最好的解决方案是使用可以显式初始化的POD类型.
或者使静态成员成为具有自己的构造函数的特定类型,该构造函数将正确初始化它.

//header

class A
{
    // Make sure this is private so that nobody can missues the fact that
    // you are overriding std::vector. Just doing it here as a quicky example
    // don't take it as a recomendation for deriving from vector.
    class MyInitedVar: public std::vector<char>
    {
        public:
        MyInitedVar()
        {
           // Pre-Initialize the vector.
           for(char c = 'a';c <= 'z';++c)
           {
               push_back(c);
           }
        }
    };
    static int          count;
    static MyInitedVar  var1;

};


//source
int            A::count = 0;
A::MyInitedVar A::var1;


归档时间:

查看次数:

151612 次

最近记录:

1 年,5 月 前