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

时间:2015-11-06 17:24:46

标签: c++ static initialization 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]);
}

如果无法做到这一点,我应该使用的变量类型是什么?我需要在运行时初始化它以及保留常量属性。

11 个答案:

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

Demo.

答案 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)

方法#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

方法#2:使用非const静态成员(如R Sahu所示):

class A {
public: 
  static int T;
};

int A::T = 0;

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

Live Demo

方法#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;
}

Live Demo

方法#4:将隐藏的非const变量声明为类的私有静态成员,并提供静态成员const引用以对其进行接口。将静态成员函数定义为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

加成:

如果只想初始化一次,可以将辅助函数更改为:

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

Live Demo

答案 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的答案是最接近于完美的,但是必须如此早地初始化变量的事实会引起一些问题:

  • 根据问题,无法使用尚不可用的数据源,例如argcargv
  • 某些依赖项可能尚未初始化。例如,许多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的适当类型作为成员,并以最小的侵入来只读访问数据。

Online demonstration.

答案 10 :(得分:-1)

在此处使用单身人士模式。 有一个数据成员,你想在单例类中的运行时初始化。创建一个实例并正确初始化数据成员,不会有覆盖它并改变它的风险。

Singleton会保留数据的单一性。

希望这有帮助。