我希望有一个带有私有静态数据成员的类(包含所有字符a-z的向量)。在java或C#中,我可以创建一个“静态构造函数”,它将在我创建类的任何实例之前运行,并设置类的静态数据成员。它只运行一次(因为变量是只读的,只需要设置一次),因为它是类的一个函数,它可以访问它的私有成员。我可以在构造函数中添加代码来检查向量是否已初始化,如果不是,则初始化它,但是这会引入许多必要的检查,并且似乎不是问题的最佳解决方案。
我想到,因为变量只是只读的,所以它们只能是公共的静态const,所以我可以在类外面设置它们,但是再一次,它看起来有点像丑陋的黑客。
如果我不想在实例构造函数中初始化它们,是否可以在类中拥有私有静态数据成员?
答案 0 :(得分:173)
要获得静态构造函数的等价物,您需要编写一个单独的普通类来保存静态数据,然后创建该普通类的静态实例。
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
};
答案 1 :(得分:76)
你可以拥有
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;
程序仍将在没有第二行的情况下链接,但初始化程序将不会被执行。
答案 2 :(得分:19)
在.h文件中:
class MyClass {
private:
static int myValue;
};
在.cpp文件中:
#include "myclass.h"
int MyClass::myValue = 0;
答案 3 :(得分:18)
从C ++ 11开始,您只需使用lambda expressions来初始化静态类成员。如果您需要在各个静态成员之间强加构造顺序,或者如果您有const
的静态成员,这甚至可以工作。
标题文件:
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();
答案 4 :(得分: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类与外部世界,保持类中包含的所有内容都被初始化。
答案 5 :(得分: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;
}
答案 6 :(得分: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_t
或char32_t
之外的任何其他类型的向量(其数组可以写为字符串文字),那么C +与C ++ 98版本相比,+ 11版本将严格删除样板代码而不引入其他语法。
答案 7 :(得分: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;
答案 8 :(得分:4)
我想简单解决这个问题的方法是:
//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;
}
答案 9 :(得分:4)
尝试编译并使用类Elsewhere
(来自Earwicker's answer)时,我得到:
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;
}
};
您也可以将参数传递给构造函数或使用特定值初始化它,它非常灵活,功能强大且易于实现...唯一的问题是您有一个包含静态变量的静态方法,而不是静态属性... syntaxis有点改变,但仍然有用。希望这对某人有用,
HugoGonzálezCastro。
答案 10 :(得分:1)
当然不需要像目前接受的答案那样复杂(Daniel Earwicker)。这堂课是多余的。在这种情况下,不需要语言战。
.hpp文件:
vector<char> const & letters();
.cpp文件:
vector<char> const & letters()
{
static vector<char> v = {'a', 'b', 'c', ...};
return v;
}
答案 11 :(得分:1)
这是另一种方法,其中向量是包含使用匿名命名空间的实现的文件的私有。它对于像实现私有的查找表这样的东西很有用:
#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;
}
答案 12 :(得分:1)
这是我的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;
}
请注意MyClass::a
仅在第[1]行存在时被初始化,因为它调用(并需要实例化)构造函数,然后需要实例化_initializer
。
答案 13 :(得分:1)
刚刚解决了同样的伎俩。我必须为Singleton指定一个静态成员的定义。 但是让事情变得更复杂 - 我已经决定我不想调用RandClass()的ctor,除非我要使用它...这就是为什么我不想在我的代码中全局初始化单例。我也添加了简单的界面。
以下是最终代码:
我简化了代码并使用rand()函数及其单种子初始化程序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();
}
答案 14 :(得分:1)
GCC优惠
__attribute__((constructor))
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
使用此属性标记一个静态方法,它将在模块加载时在main()之前运行。
答案 15 :(得分:0)
可以使用友元类或嵌套类来模拟静态构造函数,如下所示。
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
答案 16 :(得分:0)
std::call_once()
在C ++ 11中可用;如果你不能使用它,可以使用静态布尔类变量和比较和交换原子操作来完成。在构造函数中,查看是否可以自动将类静态标志从false
更改为true
,如果是,则可以运行静态构造代码。
要获得额外的功劳,请将其设为3路标志而不是布尔值,即不运行,运行和完成运行。然后该类的所有其他实例可以旋转锁定,直到运行静态构造函数的实例完成(即发出内存栅栏,然后将状态设置为“完成运行”)。你的自旋锁应该执行处理器的“暂停”指令,每次加倍等待直到达到阈值,等等 - 非常标准的自旋锁定技术。
在没有C ++ 11的情况下,this应该让你开始。
这是一些指导你的伪代码。把它放在你的班级定义中:
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();
答案 17 :(得分:0)
这是一个解决方案吗?
class Foo
{
public:
size_t count;
Foo()
{
static size_t count = 0;
this->count = count += 1;
}
};
答案 18 :(得分:0)
对于像这里这样的简单情况,包含在静态成员函数内的静态变量几乎一样好。它很简单,通常会被编译器优化掉。但这并不能解决复杂对象的初始化顺序问题。
#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();
}
答案 19 :(得分:0)
如何创建模板来模仿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
};
};
答案 20 :(得分:0)
要初始化静态变量,只需在源文件中执行此操作即可。例如:
//Foo.h
class Foo
{
private:
static int hello;
};
//Foo.cpp
int Foo::hello = 1;
答案 21 :(得分:0)
您定义静态成员变量的方式与定义成员方法的方式类似。
<强> 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;