我需要初始化私有静态对象

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

我突然想到既然变量是只读的它们可以是公共静态常量所以我可以在类外设置一次但又一次,这看起来有点像一个丑陋的黑客技术。

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

183063 次浏览

在.h 文件中:

class MyClass {
private:
static int myValue;
};

在. cpp 文件中:

#include "myclass.h"


int MyClass::myValue = 0;

定义静态成员变量的方式与定义成员方法的方式类似。

class Foo
{
public:
void bar();
private:
static int count;
};

Foo.cpp

#include "foo.h"


void Foo::bar()
{
// method definition
}


int Foo::count = 0;

要初始化静态变量,只需在源文件内部进行即可。例如:

//Foo.h
class Foo
{
private:
static int hello;
};




//Foo.cpp
int Foo::hello = 1;

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

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


};

你可以拥有

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;

该程序将仍然链接没有第二行,但初始化程序将不会执行。

Java 中静态构造函数的概念是从 C + + 的问题中吸取教训后引入的。所以我们没有直接的等价物。

最好的解决方案是使用可以显式初始化的 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;

不需要 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解决方案,但是不能在右边使用字符串文字,所以它并不完全优越。但是,如果你有一个 charwchar_tchar16_tchar32_t以外的任何其他类型的向量(其数组可以写成字符串文字) ,与 C + + 98版本相比,C + + 11版本将严格删除样板代码而不引入其他语法。

创建一个模板来模仿 C # 的行为怎么样。

template<class T> class StaticConstructor
{
bool m_StaticsInitialised = false;


public:
typedef void (*StaticCallback)(void);


StaticConstructor(StaticCallback callback)
{
if (m_StaticsInitialised)
return;


callback();


m_StaticsInitialised = true;
}
}


template<class T> bool StaticConstructor<T>::m_StaticsInitialised;


class Test : public StaticConstructor<Test>
{
static std::vector<char> letters_;


static void _Test()
{
for (char c = 'a'; c <= 'z'; c++)
letters_.push_back(c);
}


public:
Test() : StaticConstructor<Test>(&_Test)
{
// non static stuff
};
};

当尝试编译和 使用Elsewhere(从 埃尔维克的回答)时,我得到:

error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A)

如果不将一些代码放在类定义(CPP)之外,似乎不可能初始化非整数类型的静态属性。

你可以用“ 在 中包含静态局部变量的静态方法”代替它来编译:

class Elsewhere
{
public:
static StaticStuff& GetStaticStuff()
{
static StaticStuff staticStuff; // constructor runs once, single instance
return staticStuff;
}
};

你也可以把参数传递给构造函数,或者用特定的值初始化它,它非常灵活,功能强大,易于实现... ... 唯一的问题是你有一个包含静态变量的静态方法,而不是静态属性... ... 语法变化了一点,但仍然有用。希望这对某些人有用,

雨果 · 冈萨雷斯 · 卡斯特罗。

这里是另一个类似于 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 类,保持类中包含的所有内容都要初始化。

对于像这样的简单情况,包装在静态成员函数中的静态变量几乎同样好。它很简单,通常会被编译器优化掉。但这并不能解决复杂对象的初始化顺序问题。

#include <iostream>


class MyClass
{


static const char * const letters(void){
static const char * const var = "abcdefghijklmnopqrstuvwxyz";
return var;
}


public:
void show(){
std::cout << letters() << "\n";
}
};




int main(){
MyClass c;
c.show();
}

这是解决办法吗?

class Foo
{
public:
size_t count;
Foo()
{
static size_t count = 0;
this->count = count += 1;
}
};

刚刚解决了同样的问题。我必须为 Singleton 指定单个静态成员的定义。 但是让事情变得更复杂——我决定不调用 RandClass ()的 ctor,除非我要使用它... 这就是为什么我不想在我的代码中全局初始化单例。另外,我在我的案例中添加了简单的界面。

以下是最终代码:

我简化了代码并使用了 rand ()函数及其单个种子初始化器 rand ()

interface IRandClass
{
public:
virtual int GetRandom() = 0;
};


class RandClassSingleton
{
private:
class RandClass : public IRandClass
{
public:
RandClass()
{
srand(GetTickCount());
};


virtual int GetRandom(){return rand();};
};


RandClassSingleton(){};
RandClassSingleton(const RandClassSingleton&);


// static RandClass m_Instance;


// If you declare m_Instance here you need to place
// definition for this static object somewhere in your cpp code as
// RandClassSingleton::RandClass RandClassSingleton::m_Instance;


public:


static RandClass& GetInstance()
{
// Much better to instantiate m_Instance here (inside of static function).
// Instantiated only if this function is called.


static RandClass m_Instance;
return m_Instance;
};
};


main()
{
// Late binding. Calling RandClass ctor only now
IRandClass *p = &RandClassSingleton::GetInstance();
int randValue = p->GetRandom();
}
abc()
{
IRandClass *same_p = &RandClassSingleton::GetInstance();
}

下面是 EFraim 的解决方案的变体; 区别在于,由于隐式模板实例化,只有在创建了类的实例时才调用静态构造函数,而且不需要 .cpp文件中的定义(由于模板实例化的魔力)。

.h文件中,您有:

template <typename Aux> class _MyClass
{
public:
static vector<char> a;
_MyClass() {
(void) _initializer; //Reference the static member to ensure that it is instantiated and its initializer is called.
}
private:
static struct _init
{
_init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
} _initializer;


};
typedef _MyClass<void> MyClass;


template <typename Aux> vector<char> _MyClass<Aux>::a;
template <typename Aux> typename _MyClass<Aux>::_init _MyClass<Aux>::_initializer;

.cpp文件中,您可以有:

void foobar() {
MyClass foo; // [1]


for (vector<char>::iterator it = MyClass::a.begin(); it < MyClass::a.end(); ++it) {
cout << *it;
}
cout << endl;
}

请注意,只有当行[1]存在时,才会初始化 MyClass::a,因为它调用(并且需要实例化)构造函数,然后需要实例化 _initializer

这是另一个方法,其中向量对于包含实现的文件是私有的,方法是使用匿名名称空间。它对于像查找表这样的实现私有的东西很有用:

#include <iostream>
#include <vector>
using namespace std;


namespace {
vector<int> vec;


struct I { I() {
vec.push_back(1);
vec.push_back(3);
vec.push_back(5);
}} i;
}


int main() {


vector<int>::const_iterator end = vec.end();
for (vector<int>::const_iterator i = vec.begin();
i != end; ++i) {
cout << *i << endl;
}


return 0;
}

我想解决这个问题的简单方法是:

    //X.h
#pragma once
class X
{
public:
X(void);
~X(void);
private:
static bool IsInit;
static bool Init();
};


//X.cpp
#include "X.h"
#include <iostream>


X::X(void)
{
}




X::~X(void)
{
}


bool X::IsInit(Init());
bool X::Init()
{
std::cout<< "ddddd";
return true;
}


// main.cpp
#include "X.h"
int main ()
{
return 0;
}

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;
}

它当然不需要像目前公认的答案那样复杂(作者: Daniel Earwicker)。这门课是多余的。在这种情况下没有必要进行语言战争。

. hpp 文件:

vector<char> const & letters();

. cpp 文件:

vector<char> const & letters()
{
static vector<char> v = {'a', 'b', 'c', ...};
return v;
}

静态构造函数可以通过使用好友类或嵌套类进行仿真,如下所示。

class ClassStatic{
private:
static char *str;
public:
char* get_str() { return str; }
void set_str(char *s) { str = s; }
// A nested class, which used as static constructor
static class ClassInit{
public:
ClassInit(int size){
// Static constructor definition
str = new char[size];
str = "How are you?";
}
} initializer;
};


// Static variable creation
char* ClassStatic::str;
// Static constructor call
ClassStatic::ClassInit ClassStatic::initializer(20);


int main() {
ClassStatic a;
ClassStatic b;
std::cout << "String in a: " << a.get_str() << std::endl;
std::cout << "String in b: " << b.get_str() << std::endl;
a.set_str("I am fine");
std::cout << "String in a: " << a.get_str() << std::endl;
std::cout << "String in b: " << b.get_str() << std::endl;
std::cin.ignore();
}

产出:

String in a: How are you?
String in b: How are you?
String in a: I am fine
String in b: I am fine

C + + 11更新

由于 C + + 11,您可以简单地使用 Lambda 表达式来初始化静态类成员。您不再需要使用任何助手类或变通方法。

头文件:

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

源文件:

// Initialize MyClass::letters with all letters from 'a' to 'z'.
const vector<char> MyClass::letters = [] {
vector<char> letters;
for (char c = 'a'; c <= 'z'; c++)
letters.push_back(c);
return letters;
}();

关于静态初始化顺序的注意事项:

如果必须在某些 特定的顺序中初始化多个静态类成员,则此方法 还有可以工作。由于静态成员总是在 完全一样的顺序中初始化,就像在 源文件中定义的那样,您只需要确保在 源文件中以正确的顺序编写初始化。

哇,我不敢相信没有人提到这个最明显的答案,而且这个答案最接近于模仿 C # 的静态构造函数行为,也就是说,直到创建了该类型的第一个对象之后才会调用它。

std::call_once()在 C + + 11中是可用的; 如果您不能使用它,可以使用一个静态布尔类变量和一个比较和交换原子操作。在构造函数中,查看是否可以将 class-static 标志从 false自动更改为 true,如果可以,则可以运行 static-construction 代码。

为了获得额外的学分,将它设置为3-way 标志而不是 boolean,即不运行、运行和完成运行。然后,该类的所有其他实例都可以自旋锁定,直到运行静态构造函数的实例完成(例如,发出一个内存隔离,然后将状态设置为“完成运行”)。你的自旋锁应该执行处理器的“暂停”指令,每次双倍的等待直到一个阈值,等等,相当标准的自旋锁技术。

在缺少 C + + 11的情况下,这个应该可以帮助您开始。

这里有一些伪代码来指导你,把它们放到你的类定义中:

enum EStaticConstructor { kNotRun, kRunning, kDone };
static volatile EStaticConstructor sm_eClass = kNotRun;

在你的构造函数中:

while (sm_eClass == kNotRun)
{
if (atomic_compare_exchange_weak(&sm_eClass, kNotRun, kRunning))
{
/* Perform static initialization here. */


atomic_thread_fence(memory_order_release);
sm_eClass = kDone;
}
}
while (sm_eClass != kDone)
atomic_pause();

海湾合作委员会的提议

__attribute__((constructor))

Https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/function-attributes.html

用这个属性标记一个静态方法,它将在 main ()之前的模块加载时运行。