【问题标题】:Forcing static member initialisation强制静态成员初始化
【发布时间】:2011-11-01 16:27:30
【问题描述】:

我有一个类,它包含一个静态成员,一个字符串到函数指针的映射。此映射旨在使用一组静态映射填充一次,之后不会被修改。

我的问题是,如何确保地图在初始化之前不被访问?我的代码目前看起来像这样:

class MyClass
{
  static MapType s_myMap;
public:
  static const MapType& getTheMap()
  {
    if (s_myMap.empty())
    {
      // Populate the map
    }
    return s_myMap;
  }
};

这对于MyClass 的外部客户端工作正常,但不会阻止内部类成员在初始化之前直接访问private 映射。

为了解决这个问题,我正在考虑将地图设置为 getter 方法的本地:

class MyClass
{
public:
  static const MapType& getTheMap()
  {
    static MapType s_myMap;
    if (s_myMap.empty())
    {
      // Populate the map
    }
    return s_myMap;
  }
};

这是个好主意,还是有更好的方法来实现这一点?

【问题讨论】:

  • 将静态元素移到函数中是个好主意。只要没有其他静态对象初始化调用getTheMap,就可以了。
  • 地图是动态初始化的吗?如果没有,您可以将其填充到初始化程序中(前提是您有最新的编译器)。
  • @KerrekSB 它是静态初始化的。只是为了检查我是否理解您,您是否建议将// Populate the map 隐含的代码移动到单独的初始化方法(例如initialiseMap()),然后通过MapType MyClass::s_myMap = initialiseMap() 初始化地图?
  • @atkins:我只会写 const MapType MyClass::s_myMap{ { 1, "Jim"}, { 2, "Jane" }, { 3, "Joseph" } }; 作为初始化器,没有单独的函数来执行填充。
  • @KerrekSB 唉,看来我的编译器不支持这种语法(我们被困在 MSVC 2003 上)。我得到error C2470: 'MyClass::s_myMap' : looks like a function definition, but there is no formal parameter list; skipping apparent body。不过感谢您的想法!

标签: c++ initialization static-members


【解决方案1】:

将静态元素移动到函数中将解决任何顺序 初始化问题,但它可能会给你留下一个订单 破坏一。在许多情况下,最好使用指针和 动态分配,让map永远不会被破坏。

至于初始化,我经常使用两个迭代器构造函数,所以 我可以使地图本身成为常量。为此,只需定义一个 struct 带有转换运算符,例如:

struct MapInitData
{
    char const* key;      //  Or whatever type is needed.
    char const* value;    //  Or whatever type is needed.
    operator MapType::value_type() const
    {
        return MapType::value_type( key, value );
    }
};

MapInitData const mapInitTable[] =
{
    { "key1", "value1" },
    //  ...
};

MapType const ourMap( begin( mapInitTable ), end( mapInitTable ) );

【讨论】:

  • 太好了——我真的很想制作地图const,但没想到我能做到。
  • 我讨厌这个建议(关于指针和动态分配)。破坏顺序问题很容易解决。 stackoverflow.com/questions/335369/….
  • @LokiAstari 我看不到那里的代码可以解决任何问题。销毁的顺序仅在对象的特定子集内排序,例如静态声明的对象。假设您有一个static std::auto_ptr<Something>,并且在它构造很久之后,您将它重置为指向一个在其析构函数中使用该静态对象的对象。 (这种情况并不常见——至少希望如此。但有可能。)
  • @James Kanze:of static storage duration objects 的破坏顺序保证与构造顺序相反。我根本看不出您的示例(在 cmets 中)有何相关性。
  • @LokiAstari 保证静态存储时长的对象的销毁顺序。但并非所有对象都有静态存储持续时间。考虑类似 'static auto_ptr p;静态对象; int main() { p = auto_ptr new T( &o ); }. What happens if T` 在其析构函数中使用传递给它的参数?
【解决方案2】:

您可以为此集合声明一个类并在构造函数中填充它。

【讨论】:

    【解决方案3】:

    如果MyClass::getTheMap() 没有在全局/namespace 范围内调用,那么您不必担心在初始化之前使用static 数据成员。

    但是,如果上面的static方法getTheMap()用于全局/namespace范围:

    SomeGlobal object = MyClass::getTheMap();
    

    那么你目前的方法似乎没问题。

    【讨论】:

    • 我对“初始化”这个词有点松懈。我在这里的意思是地图应该在被访问之前填充一组条目。使用MapType MyClass::s_myMap = MapType() 将数据成员初始化为更精确的空映射。
    • @atkins:如果您没有首先进入未定义的行为,那么getTheMap() 将正确设置条目。问题在于,使用全局静态变量真的很容易陷入未定义的行为。
    【解决方案4】:

    为了线程安全,您的函数“getTheMap”应该有一个锁。将其移动到函数的本地是个好主意。

    我认为这与 drdobbs 文章“C++ and The Perils of Double-Checked Locking”http://drdobbs.com/cpp/184405726 的问题类似。在这里,他介绍了不同类型的单例和其他具有线程安全性的模式的用例和安全性。

    至于毁灭,你需要毁灭它吗?

    【讨论】:

      猜你喜欢
      • 2011-09-19
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      • 2011-07-18
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      相关资源
      最近更新 更多