我可以在C ++中的运行时初始化静态const成员吗?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我可以在C ++中的运行时初始化静态const成员吗?相关的知识,希望对你有一定的参考价值。
是否可以在运行时初始化我的类的静态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]);
}
如果无法做到这一点,我应该使用的变量类型是什么?我需要在运行时初始化它以及保留常量属性。
我很遗憾不同意这些评论和答案,说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()启动时,静态符号已经初始化。
我自己最近遇到了同样的问题,我发现@ A.S.H的答案是最接近完美的,但变量必须如此早地初始化会导致一些问题:
- 不能使用尚不可用的数据源,例如
argc
和argv
。 - 某些依赖项可能尚未初始化。例如,许多GUI框架不允许在早期创建文本框。这是一个问题,因为如果加载配置文件无法通知用户,我们可能希望显示错误文本框。
所以我想出了以下内容:
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
作为成员,并且只读访问数据的入侵最少。
在这里使用单身模式。有一个数据成员,你想在单例类中的运行时初始化。创建一个单个实例并正确初始化数据成员,不会有覆盖它并改变它的风险。
Singleton会保留数据的奇异性。
希望这可以帮助。
你不能依赖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;
}
不,你做不到。
如果无法做到这一点,我应该使用哪种变量?
您可以使用非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;
}
不仅你不能,你不应该通过搞乱const_cast来尝试这样做。静态const成员很有可能以只读段结束,任何修改它们的尝试都会导致程序崩溃。
通常,您将拥有多个配置值。所以把它们放在一个struct中,对它的正常全局访问是const。
const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}
您可以获得更高级的功能并具有返回配置的全局功能,因此普通代码甚至无法更改指针,但是偶然地更难做到这一点。
头文件公开get_config ()
供所有人使用,但设置它的方式只有那些意图的代码知道。
不,因为您将变量定义为static和const,所以无法更改其值。您必须在定义本身中设置其值,或者通过在创建类A的对象时调用的构造函数来设置它的值。
方法#1:初始化一个隐藏的非const变量,并提供一个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;
}
方法#2:使用非const
静态成员(如R Sahu所示):
class A {
public:
static int T;
};
int A::T = 0;
int main() {
A::T = 10;
}
方法#3:将隐藏的非const变量声明为类的私有静态成员,并提供静态成员const引用以接口它。将朋友函数定义为初始化程序:
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;
}
方法#4:将隐藏的非const变量声明为类的私有静态成员,并提供静态成员const引用以将其接口。将静态成员函数定义为初始化程序:
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;
}
嗯你
所需内容的语义都是错误的,你不应该使用static-const。
静态是具有静态存储持续时间和内部链接的对象或整体类型。
const是一个在整个应用程序生命周期内不会改变其值的对象,任何改变它的尝试都会产生UD。 (绝大多数此类案件是一个非常明确的崩溃)
由于这个问题,已经提出了危险的解决方法来模仿隐含的行为。在大多数示例中,静态const-reference被赋予某种隐藏的静态,其可在运行时分配,例如, 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;
};
如何处理第二次调用的情况,即实现决策。在此示例中,该值完全被忽略。其他人可能更喜欢抛出异常,做出断言,......等等。
有一个技巧,但你应该避免它!这是一个简单的例子来说明原理:
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..
}
小心!
以上是关于我可以在C ++中的运行时初始化静态const成员吗?的主要内容,如果未能解决你的问题,请参考以下文章