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

问题描述 投票:20回答:11

是否可以在运行时初始化我的类的静态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]);
}

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

c++ static initialization const
11个回答
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()启动时,静态符号已经初始化。


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.


-1
投票

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

Singleton会保留数据的奇异性。

希望这可以帮助。


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.


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

5
投票

不仅你不能,你不应该通过搞乱const_cast来尝试这样做。静态const成员很有可能以只读段结束,任何修改它们的尝试都会导致程序崩溃。


4
投票

通常,您将拥有多个配置值。所以把它们放在一个struct中,对它的正常全局访问是const。

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

您可以获得更高级的功能并具有返回配置的全局功能,因此普通代码甚至无法更改指针,但是偶然地更难做到这一点。

头文件公开get_config ()供所有人使用,但设置它的方式只有那些意图的代码知道。


3
投票

不,因为您将变量定义为static和const,所以无法更改其值。您必须在定义本身中设置其值,或者通过在创建类A的对象时调用的构造函数来设置它的值。


2
投票

方法#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引用以将其接口。将静态成员函数定义为初始化程序:

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


0
投票

嗯你

所需内容的语义都是错误的,你不应该使用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;
};

如何处理第二次调用的情况,即实现决策。在此示例中,该值完全被忽略。其他人可能更喜欢抛出异常,做出断言,......等等。


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

小心!

© www.soinside.com 2019 - 2024. All rights reserved.