简体   繁体   English

我可以在C ++中的运行时初始化静态const成员吗?

[英]Can I initialize a static const member at run-time in C++?

Is it possible to initialize a static const member of my class during run-time? 是否可以在运行时初始化我的类的静态const成员? This variable is a constant throughout my program but I want to send it as a command-line argument. 这个变量在我的程序中是一个常量,但我想将它作为命令行参数发送。

//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]);
}

If this cannot be done, what is the type of variable I should use? 如果无法做到这一点,我应该使用的变量类型是什么? I need to initialize it at run-time as well as preserve the constant property. 我需要在运行时初始化它以及保留常量属性。

You cannot rely on data produced after your main has started for initialization of static variables, because static initialization in the translation unit of main happens before main gets control, and static initialization in other translation units may happen before or after static initialization of main translation unit in unspecified order. 你不能依赖main开始初始化static变量后产生的数据,因为main的转换单元中的静态初始化发生在main得到控制之前,而其他转换单元中的静态初始化可能发生在main转换单元的静态初始化之前或之后以未指定的顺序。

However, you can initialize a hidden non-const variable, and provide a const reference to it, like this: 但是,您可以初始化隐藏的非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;
}

Demo. 演示。

I am sorry to disagree with the comments and answers saying that it is not possible for a static const symbol to be initialized at program startup rather than at compile time. 我很遗憾不同意这些评论和答案,说static const符号不可能在程序启动时而不是在编译时初始化。

Actually this IS possible, and I used it many times, BUT I initialize it from a configuration file. 实际上这是可能的,我使用了很多次,但我从配置文件初始化它。 Something like: 就像是:

// GetConfig is a function that fetches values from a configuration file
const int Param1 = GetConfig("Param1");
const int MyClass::Member1 = GetConfig("MyClass.Member1");

As you see, these static consts are not necessarily known at compile time. 如您所见,这些静态consts在编译时不一定是已知的。 They can be set from the environment, such as a config file. 它们可以从环境中设置,例如配置文件。

On the other hand, setting them from argv[], seems very difficult, if ever feasible, because when main() starts, static symbols are already initialized. 另一方面,如果可行的话,从argv []设置它们似乎非常困难,因为当main()启动时,静态符号已经初始化。

No, you cannot do that. 不,你做不到。

If this cannot be done what is the type of variable I should use ? 如果无法做到这一点,我应该使用哪种变量?

You can use a non- const member. 您可以使用非const成员。

class A 
{
   public: 
      static int T;
};

int A::T;

Another option is to make T a private member, make main a friend so only it can modify the value, and then expose the member through a function. 另一个选择是使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;
}

Not only you can't, you should not try doing this by messing with const_cast. 不仅你不能,你不应该通过搞乱const_cast来尝试这样做。 Static const members have a very high chance of ending up in read-only segment, and any attempt to modify them will cause program to crash. 静态const成员很有可能以只读段结束,任何修改它们的尝试都会导致程序崩溃。

Typically you will have more than one configuration value. 通常,您将拥有多个配置值。 So put them in a struct, and the normal global access to it is const. 所以把它们放在一个struct中,对它的正常全局访问是const。

const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}

You can get fancier and have a global function to return config, so normal code can't even change the pointer, but it is harder to do that by accident. 您可以获得更高级的功能并具有返回配置的全局功能 ,因此普通代码甚至无法更改指针,但是偶然地更难做到这一点。

A header file exposes get_config () for all to use, but the way to set it is only known to the code that's meant to do so. 头文件公开get_config ()供所有人使用,但设置它的方式只有那些意图的代码才知道。

No, since you defined the variable as static and const, you cannot change its value. 不,因为您将变量定义为static和const,所以无法更改其值。 You will have to set its value in the definition itself, or through a constructor called when you create an object of class A. 您必须在定义本身中设置其值,或者通过在创建类A的对象时调用的构造函数来设置它的值。

Method #1: Initialize a hidden non-const variable, and provide a const reference to it (as shown by dasblinkenlight): 方法#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;
}

Live Demo 现场演示

Method #2: Use a non const static member (as shown by R Sahu): 方法#2:使用非const静态部件(如图所示由R萨胡):

class A {
public: 
  static int T;
};

int A::T = 0;

int main() {
  A::T = 10;
}

Live Demo 现场演示

Method #3: Declare a hidden non-const variable as a private static member of your class and provide a static member const reference to interface it. 方法#3:将隐藏的非const变量声明为类的私有静态成员,并提供静态成员const引用以接口它。 Define a friend function as inititalizer: 将朋友函数定义为初始化程序:

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

Live Demo 现场演示

Method #4: Declare a hidden non-const variable as a private static member of your class and provide a static member const reference to interface it. 方法#4:将隐藏的非const变量声明为类的私有静态成员,并提供静态成员const引用以将其接口。 Define a static member function as inititalizer: 将静态成员函数定义为初始化程序:

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

Live Demo 现场演示

Bonus: 奖金:

If you want to initialize only once you can change the helper function to: 如果只想初始化一次,可以将辅助函数更改为:

static void foo(int val) { 
  static bool init = true;
  if(init) A::dummy = val;
  init = false;
}

Live Demo 现场演示

N - O N - O.

The semantics of what is being required are all wrong, and you shouldn't use a static-const for that. 所需内容的语义都是错误的,你不应该使用static-const

A static is an object or integral type which has static storage duration and internal linkage. 静态是具有静态存储持续时间和内部链接的对象或整体类型。

A const is an object that does not change its value throughout application's lifetime, any attempt to change it results in UD . const是一个在整个应用程序生命周期内不会改变其值的对象,任何改变它的尝试都会产生UD。 ( the overwhelming majority of such cases is a pretty well defined crash ) 绝大多数此类案件是一个非常明确的崩溃

As a result of this question dangerous workarounds have been proposed to mimic the implied behavior. 由于这个问题,已经提出了危险的解决方法来模仿隐含的行为。 In most of examples a static-const-reference is given a somehow hidden static which is assignable at runtime, eg this . 在大多数示例中, static-const-reference被赋予某种隐藏的静态 ,可以在运行时分配,例如

Apart from the difficulties in maintaining such code, the problem remains that declared semantics are not actually enforced. 除了维护此类代码的困难之外,问题仍然是声明的语义实际上并未实施。

For example in keeping the value const throughout the application runtime can be hacked by doing const_cast<int &>(A::T) = 42 , which is perfectly valid, perfectly define code since the referenced type is not const. 例如,保持整个应用程序运行时的值const可以通过执行const_cast<int &>(A::T) = 42 ,这是完全有效的,完美定义代码,因为引用的类型不是const。

What is being sought after here is an class that permits to be initialized only once throughout the application, has internal linkage, and the lifetime of the application. 在此之后所寻求的是允许在整个应用程序中仅初始化一次,具有内部链接以及应用程序的生命周期的类。

So just do a template class that does that: 所以只需要做一个模板类来做到这一点:

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

How to handle the case that it is called a second time, that is an implementation decision. 如何处理第二次调用的情况,即实现决策。 In this example the value is totally ignored. 在此示例中,该值完全被忽略。 Others may prefer to throw an exception, do an assertion, ... etc. 其他人可能更喜欢抛出异常,做出断言,......等等。

There is a trick, but you should probably avoid it! 有一个技巧,但你应该避免它! Here's a bare bones example to illustrate the principle: 这是一个简单的例子来说明原理:

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

Careful! 小心!

Having been facing the same problem myself lately I found @ASH 's answer to be the closest to perfect but the fact that the variables have to be initialized so early can cause some problems: 我自己最近遇到了同样的问题,我发现@ASH的答案是最接近完美的,但变量必须如此早地初始化会导致一些问题:

  • Can't use data sources that aren't available yet, such as argc and argv as per the question. 不能使用尚不可用的数据源,例如argcargv
  • Some dependencies might not be initialized yet. 某些依赖项可能尚未初始化。 For example, many a GUI framework does not allow creating textboxes that early on yet. 例如,许多GUI框架不允许在早期创建文本框。 This is a problem because we might want to display a error textbox if loading the configuration file fails to inform the user. 这是一个问题,因为如果加载配置文件无法通知用户,我们可能希望显示错误文本框。

So I came up with the following: 所以我想出了以下内容:

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;

We make our data static but non-const so we don't have to initialize it immediately and can assign the correct values to it at a more opportune time. 我们将数据设置为静态但非常量,因此我们不必立即对其进行初始化,并可以在更合适的时间为其分配正确的值。 Read only access is given trough a overload of operator -> The default constructor checks if a StaticConfig of this type has already been loaded with valid data and throws if it is not. 通过operator ->的重载给出只读访问operator ->默认构造函数检查此类型的StaticConfig是否已加载有效数据,如果不是则抛出。 This should never happen in practice but serves as a debugging aid. 这绝不应该在实践中发生,而是作为调试辅助工具。 A private constructor allows loading the type with valid data. 私有构造函数允许使用有效数据加载类型。 A ConfigHandler class, responsible for loading the data, is made a friend so it can access the private constructor. 负责加载数据的ConfigHandler类成为朋友,因此它可以访问私有构造函数。

A ConfigHandler instance can be briefly created at an opportune time when all the dependencies are available to initialize all the StaticConfig types. 当所有依赖项都可用于初始化所有StaticConfig类型时,可以在适当的时候简要地创建ConfigHandler实例。 Once done, the ConfigHandler instance can be discarded. 完成后,可以丢弃ConfigHandler实例。 After that, a class can simply include the appropriate type of StaticConfig as a member and read-only access the data with minimal intrusion. 之后,类可以简单地包含适当类型的StaticConfig作为成员,并且只读访问数据的入侵最少。

Online demonstration. 在线演示。

Use a Singleton Pattern here. 在这里使用单身模式。 have a data member which you'd like to initialize at run time in the singleton class. 有一个数据成员,你想在单例类中的运行时初始化。 One a single instance is created and the data member is properly initialized, there would be no further risk of overwriting it and altering it. 创建一个单个实例并正确初始化数据成员,不会有覆盖它并改变它的风险。

Singleton would preserve the singularity of your data. Singleton会保留数据的奇异性。

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM