静态成员在仍有未完成的实例时被销毁?

时间:2011-05-09 21:16:01

标签: c++ segmentation-fault destructor static-members

我需要从析构函数访问静态数据成员,但是在程序退出时似乎无法保证它仍然存在!出于某种原因,静态成员正在被销毁,同时仍有未完成的类实例。这很奇怪,因为我以前从未听过“绝不会从析构函数中访问静态成员”的建议,但我认为如果它存在,我就会知道这样的限制。

我将举一个具体的例子:

class MyClass {
    public:
        ~MyClass() { m_instances.erase(m_name); }

    private:
        long m_name;
        static std::map<long, MyClass*> m_instances;
};

在另一个课程中,我尝试了以下令人讨厌的黑客攻击,但是当我考虑它时,我认为它根本不是解决方案。

class MyClass {
    friend class Switch;

    public:
        ~MyClass() { if (m_alive) m_instances.erase(m_name); }

    private:

        static bool m_alive;
        class Switch {
            ~Switch() { MyClass::m_alive = false; }
        };
        static Switch m_switch;

        long m_name;
        static std::map<long, MyClass*> m_instances;
};

如果在m_instances之后但在m_switch之前销毁了MyClass的实例,该怎么办?即使m_switch首先死掉,布尔m_alive可能已被“破坏”,因此可能被覆盖为'true'(不太可能,我知道)。

那么有人能提供更好的解决方案吗?我希望我在这里找不到一些非常明显的东西。

3 个答案:

答案 0 :(得分:2)

无法保证仅在同一类的对象的所有实例之后销毁静态成员。 C ++没有引用计数范例(尽管shared_ptr)。

在考虑生命周期时,请将静态成员视为任何其他静态对象。除了在类的“命名空间”(警告:不准确的术语)之外,实际上没有任何东西将它们绑定到它们的包含类。

因此,如果您的myClass实例也是静态创建的,那么您需要考虑它们之间的正常静态对象生存期规则:

Example #1

#include <iostream>

struct A {
    A() { std::cout << "*A "; };
   ~A() { std::cout << "~A "; };
};

struct B {
    B() { std::cout << "*B "; };
   ~B() { std::cout << "~B "; };

    static A a;
};

B t;
A B::a;

int main() {}

// Output: *B *A ~A ~B 

如您所见,B的静态成员在B的实际实例之前被销毁。

Example #2:

#include <iostream>

struct A {
    A() { std::cout << "*A "; };
   ~A() { std::cout << "~A "; };
};

struct B {
    B() { std::cout << "*B "; };
   ~B() { std::cout << "~B "; };

    static A a;
};

A B::a;
B t;

int main() {}

// Output: *A *B ~B ~A 

这反过来也是如此。对于您当前的问题,这是一个廉价的解决方案,但我的建议是完全避免静态实例;你只会陷入更加静态的初始化陷阱,就像这样......可能会有未来相同代码的化身!

答案 1 :(得分:2)

这显然是静态销毁订单的问题。我会推荐以下内容:

class MyClass {
    public:
        MyClass() { add_instance(m_name, this); };
        ~MyClass() { erase_instance(m_name); }

    private:
        long m_name;
        static std::map<long, MyClass*>& get_instance_map();
        static void add_instance(long aName, MyClass* aObj);
        static void erase_instance(long aName);
};

std::map<long, MyClass*>* MyClass::get_instance_map() {
  static std::map<long, MyClass*>* p_inst = new std::map<long, MyClass*>();
  return p_inst;
};

void MyClass::add_instance(long aName, MyClass* aObj) {
  static std::map<long, MyClass*>* p_inst = MyClass::get_instance_map();
  p_inst->insert( std::make_pair(aName, aObj) );
};

void MyClass::erase_instance(long aName) {
  static std::map<long, MyClass*>* p_inst = MyClass::get_instance_map();
  p_inst->erase( aName );
};

如果您需要删除实例映射,则可能无法实现。否则,只需使用普通的构造首次使用的习语。这里的要点是map是一个堆分配的std :: map对象,而不是删除它只是意味着它将被刷新,因为OS回收了在每次其他“正常”执行之后发生的freestore内存,就像析构函数。

答案 2 :(得分:0)

如果你遇到静态的这类问题,那就必然意味着MyClass也有静态范围,你不能用一个静态访问另一个来设计这样的代码。它可能会起作用,因为你对破坏的顺序有问题,它可能不起作用。

完全有可能你有另一个全局(静态)导致内存损坏。如果是这种情况,则可能意味着覆盖一个全局可能会覆盖位于同一内存空间中的全局变量附近的其他全局变量,即您遇到问题的静态已损坏且未被删除。