静态构造函数在C + +? 我需要初始化私有静态对象
我想有一个私有的静态数据成员(包含所有字符az的向量)的类。 在java或C#中,我可以创build一个“静态构造函数”,它将在我创build类的任何实例之前运行,并设置类的静态数据成员。 它只运行一次(因为variables是只读的,只需要设置一次),因为它是类的函数,它可以访问其私有成员。 我可以在构造函数中添加代码,以检查向量是否已初始化,如果不是,则会初始化代码,但引入了许多必要的检查,看起来并不是问题的最佳解决scheme。
我想到,由于variables是只读的,所以它们可以是公共的静态常量,所以我可以在课堂以外设置它们,但是再一次,它看起来有点像一个丑陋的黑客。
如果我不想在实例构造函数中初始化它们,是否有可能在类中有私有静态数据成员?
要获得静态构造函数的等价物,需要编写一个单独的普通类来保存静态数据,然后创build该普通类的静态实例。
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;
程序仍然没有第二行链接,但是初始化程序不会被执行。
在.h文件中:
class MyClass { private: static int myValue; };
在.cpp文件中:
#include "myclass.h" int MyClass::myValue = 0;
这里是另一种类似于丹尼尔·埃里克的方法,也是使用康拉德·鲁道夫的朋友级build议。 这里我们使用一个内部的私人实用工具类来初始化你的主类的静态成员。 例如:
头文件:
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); }
这种方法的优点是完全隐藏了外部世界的初始化类,保持类中包含的所有内容都被初始化。
不需要init()
函数, std::vector
可以从一个范围创build:
// 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 ) );
但是请注意,types的静态types会在图书馆中造成麻烦,所以应该避免在那里。
C ++ 11更新
从C ++ 11开始,你可以这样做:
// cpp file: std::vector<char> MyClass::alphabet = { 'a', 'b', 'c', ..., 'z' };
它在语义上等同于原始答案中的C ++ 98解决scheme,但不能在右侧使用string文字,因此它不是完全优越的。 但是,如果你有一个除char
, wchar_t
, char16_t
或char32_t
(其数组可以写成string文字)以外的任何其他types的向量,C ++ 11版本将严格删除样板代码,而不会引入其他语法, C ++ 98版本。
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; }
C ++ 11解决scheme
由于C ++ 11,一个静态构造函数可以简单地实现为lambda函数:
头文件:
class Foo { static Bar staticMember1; static Bar staticMember2; static const bool StaticCtor; // Note: must be the last static member };
源文件:
Bar Foo::staticMember1; Bar Foo::staticMember2; const bool Foo::StaticCtor = [] { staticMember1 = ... ; staticMember2 = ... ; return true; // Note: unused dummy return value }();
静态构造函数的概念是在Java中从C ++中的问题中得到的。 所以我们没有直接的等价物。
最好的解决scheme是使用可以明确初始化的PODtypes。
或者让你的静态成员具有自己的构造函数,将正确初始化它的特定types。
//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;
当试图编译和使用 Elsewhere
类(来自Earwicker的答案 )时,我得到:
error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A)
似乎是不可能初始化非整数types的静态属性,而不需要在类定义(CPP)之外放置一些代码。
为了进行编译,你可以使用“ 静态局部variables的方法 ”。 像这样的东西:
class Elsewhere { public: static StaticStuff& GetStaticStuff() { static StaticStuff staticStuff; // constructor runs once, single instance return staticStuff; } };
你也可以传递参数给构造函数,或者用特定的值初始化它,这是非常灵活,强大和容易实现的…唯一的事情是你有一个静态方法包含一个静态variables,而不是一个静态属性…语法修改了一下,但仍然有用。 希望这对某个人有用,
雨果·冈萨雷斯·卡斯特罗。
我想这个简单的解决scheme将是:
//Xh #pragma once class X { public: X(void); ~X(void); private: static bool IsInit; static bool Init(); }; //X.cpp #include "Xh" #include <iostream> X::X(void) { } X::~X(void) { } bool X::IsInit(Init()); bool X::Init() { std::cout<< "ddddd"; return true; } // main.cpp #include "Xh" int main () { return 0; }
刚解决同样的伎俩。 我不得不为Singleton指定单个静态成员的定义。 但是让事情变得更加复杂 – 我已经决定,除非我要使用它,否则我不想调用RandClass()的ctor。这就是为什么我不想在代码中全局初始化singleton的原因。 另外我在我的情况下添加了简单的界面。
这是最后的代码:
我简化了代码,并使用rand()函数和它的单个种子initialzer srand()
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(); }
下面是另一种方法,其中vector对于包含使用匿名名称空间的实现的文件是私有的。 这对查找表是非常有用的。
#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; }
它当然不需要像目前被接受的答案那样复杂(Daniel Earwicker)。 这个class是多余的。 在这种情况下,不需要语言战争。
.hpp文件:
vector<char> const & letters();
.cpp文件:
vector<char> const & letters() { static vector<char> v = {'a', 'b', 'c', ...}; return v; }
您可以像定义成员方法一样定义静态成员variables。
foo.h中
class Foo { public: void bar(); private: static int count; };
Foo.cpp中
#include "foo.h" void Foo::bar() { // method definition } int Foo::count = 0;
要初始化静态variables,只需在源文件中进行。 例如:
//Foo.h class Foo { private: static int hello; }; //Foo.cpp int Foo::hello = 1;
如何创build一个模板来模仿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 }; };
对于像这里这样简单的情况,一个静态成员函数中包含的静态variables几乎是一样的好。 这很简单,通常会被编译器优化。 这并不能解决复杂对象的初始化顺序问题。
#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(); }
这是一个解决scheme吗?
class Foo { public: size_t count; Foo() { static size_t count = 0; this->count = count += 1; } };
这是我EFraim的解决scheme的变种; 不同之处在于,由于隐式模板实例化,静态构造函数仅在创build类的实例时才被调用,并且不需要.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
。
静态构造函数可以通过使用下面的朋友类或嵌套类来模拟。
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