是否可以在运行时初始化我的类的静态const成员?这个变量在我的程序中是一个常量,但我想将它作为命令行参数发送。
//A.h
class A {
public:
static const int T;
};
//in main method
int main(int argc,char** argv)
{
//how can I do something like
A::T = atoi(argv[1]);
}
如果无法做到这一点,我应该使用的变量类型是什么?我需要在运行时初始化它以及保留常量属性。
答案 0 :(得分:30)
您不能依赖main
开始初始化static
变量后生成的数据,因为main
的翻译单元中的静态初始化发生在main
获得控制权之前其他翻译单元中的静态初始化可能在main
翻译单元的静态初始化之前或之后以未指定的顺序发生。
但是,您可以初始化隐藏的非const变量,并为其提供const
引用,如下所示:
struct A {
public:
// Expose T as a const reference to int
static const int& T;
};
//in main.cpp
// Make a hidden variable for the actual value
static int actualT;
// Initialize A::T to reference the hidden variable
const int& A::T(actualT);
int main(int argc,char** argv) {
// Set the hidden variable
actualT = atoi(argv[1]);
// Now the publicly visible variable A::T has the correct value
cout << A::T << endl;
}
答案 1 :(得分:10)
我很遗憾不同意这些评论和答案,说明在程序启动时而不是在编译时初始化static const
符号是不可能的。
实际上这是可能的,我多次使用它,但我从配置文件初始化它。类似的东西:
// GetConfig is a function that fetches values from a configuration file
const int Param1 = GetConfig("Param1");
const int MyClass::Member1 = GetConfig("MyClass.Member1");
如您所见,这些静态consts在编译时不一定是已知的。可以从环境中设置它们,例如配置文件。
另一方面,如果可行的话,从argv []设置它们似乎非常困难,因为当main()启动时,静态符号已经初始化。
答案 2 :(得分:6)
不,你做不到。
如果无法做到这一点,我应该使用哪种变量?
您可以使用非const
成员。
class A
{
public:
static int T;
};
int A::T;
另一个选择是让T
成为私人成员,让main
成为朋友,这样只有它可以修改价值,然后通过函数公开成员。
#include <cstdlib>
class A
{
public:
static int getT() { return T; }
private:
static int T;
friend int main(int argc, char** argv);
};
int A::T;
int main(int argc, char** argv)
{
A::T = std::atoi(argv[1]);
return 0;
}
答案 3 :(得分:5)
不仅你不能,你不应该通过搞乱const_cast来尝试这样做。静态const成员很有可能以只读段结束,任何修改它们的尝试都会导致程序崩溃。
答案 4 :(得分:4)
通常,您将拥有多个配置值。所以把它们放在一个struct中,对它的正常全局访问是const。
const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}
你可以得到更好的并且有一个全局的函数来返回配置,所以普通的代码甚至不能改变指针,但是偶然的做法更难。
一个头文件公开get_config ()
供所有人使用,但设置它的方式只有代码才知道。
答案 5 :(得分:3)
不,因为您将变量定义为static和const,所以无法更改其值。 您必须在定义本身中设置其值,或者通过在创建A类对象时调用的构造函数来设置它。
答案 6 :(得分:1)
const
引用(如dasblinkenlight所示):class A {
public:
static const int &T;
};
static int dummy = 0;
const int &A::T = dummy;
int main() {
dummy = 10;
std::cout << A::T << std::endl;
}
const
静态成员(如R Sahu所示):class A {
public:
static int T;
};
int A::T = 0;
int main() {
A::T = 10;
}
class A {
friend void foo(int);
static int dummy;
public:
static const int &T;
};
const int &A::T = A::dummy;
int A::dummy = 0;
void foo(int val) { A::dummy = val; }
int main() {
foo(10);
std::cout << A::T << std::endl;
}
class A {
static int dummy;
public:
static const int &T;
static void foo(int val) { A::dummy = val; }
};
const int &A::T = A::dummy;
int A::dummy = 0;
int main() {
A::foo(10);
std::cout << A::T << std::endl;
}
如果只想初始化一次,可以将辅助函数更改为:
static void foo(int val) {
static bool init = true;
if(init) A::dummy = val;
init = false;
}
答案 7 :(得分:0)
N - O
所需内容的语义都是错误的,你不应该使用 static-const 。
静态是具有静态存储持续时间和内部链接的对象或整数类型。
const 是一个在整个应用程序生命周期内不会更改其值的对象,任何更改它的尝试都会导致UD。 (绝大多数此类案件是一个非常明确的崩溃)
由于这个问题,已经提出了危险的解决方法来模仿隐含的行为。在大多数示例中, static-const-reference 以某种方式隐藏 static ,它可以在运行时分配,例如, this。
除了维护此类代码的困难之外,问题仍然是声明的语义实际上并未实施。
例如,保持整个应用程序运行时的值const可以通过执行完全有效的const_cast<int &>(A::T) = 42
来攻击,完美定义代码,因为引用的类型不是const。
此后正在寻找什么是一个允许在整个应用程序中初始化一次,具有内部链接和应用程序生命周期的类。
所以只需做一个模板类来做到这一点:
template<typename V> class fixation
{
bool init = true;
V val;
public:
fixation(V const & v) : init(true), val(v) {}
fixation & operator=( fixation arg)
{
if(init )
{
this->val = arg.val;
}
this->init = false;
return *this;
}
V get()
{
return val;
}
};
struct A
{
static fixation<int> T;
};
如何处理第二次调用它的情况,这是一个实现决策。在此示例中,该值完全被忽略。其他人可能更喜欢抛出异常,做出断言,等等。
答案 8 :(得分:0)
有一个技巧,但你应该避免它!这里有一个简单的例子来说明这个原则:
int const& foo(int i) {
static const int j = (i == 0 ? throw 0 : i);
return j;
}
int main() {
try {
int x = foo(0); // oops, we throw
} catch(...) {}
int x = foo(1); // initialized..
int y = foo(0); // still works..
}
小心!
答案 9 :(得分:0)
最近我自己也遇到了同样的问题,我发现@ A.S.H的答案是最接近于完美的,但是必须如此早地初始化变量的事实会引起一些问题:
argc
和argv
。所以我提出了以下建议:
template <class T>
class StaticConfig
{
public:
StaticConfig()
{
if (!mIsInitialised)
{
throw std::runtime_error("Tried to construct uninitialised StaticConfig!");
}
}
const T*
operator -> () const
{
return &mConfig;
}
private:
friend class ConfigHandler;
StaticConfig(const T& config)
{
mConfig = config;
mIsInitialised = true;
}
static T mConfig;
static bool mIsInitialised;
};
template <class T>
T StaticConfig<T>::mConfig;
template <class T>
bool StaticConfig<T>::mIsInitialised = false;
我们将数据设为静态但非常量,因此我们不必立即对其进行初始化,并且可以在更合适的时间为其分配正确的值。通过重载operator ->
授予只读访问权限默认构造函数检查此类型的StaticConfig
是否已装载有效数据,如果没有,则抛出该异常。在实践中,这绝对不应该发生,但可以作为调试的辅助工具。私有构造函数允许使用有效数据加载类型。负责加载数据的ConfigHandler
类成为了朋友,因此它可以访问私有构造函数。
当所有依赖项都可用于初始化所有ConfigHandler
类型时,可以在适当的时候短暂创建一个StaticConfig
实例。完成后,ConfigHandler
实例可以被丢弃。之后,一个类可以简单地将StaticConfig
的适当类型作为成员,并以最小的侵入来只读访问数据。
答案 10 :(得分:-1)
在此处使用单身人士模式。 有一个数据成员,你想在单例类中的运行时初始化。创建一个实例并正确初始化数据成员,不会有覆盖它并改变它的风险。
Singleton会保留数据的单一性。
希望这有帮助。