2013-02-24 3 views
0

Я наткнулся на довольно неприятную проблему, связанную с тем, что среда выполнения Visual C++ разрушает мои объекты по мере выхода программы.Порядок уничтожения объекта Visual C++

У меня есть класс, который используется для обеспечения ссылки на определенные состояния. Мне нужно сохранить состояния в течение неизвестного времени, и за это время состояние может быть уничтожено, и я не могу использовать shared_ptr. Поэтому я использую

class MyClass 
{ 
private: 
    static std::list<MyClass*> make_list(); 
    static std::list<MyClass*> refed_list;   
    static void StateClosed(B* state); 

public: 
    B* state; 
    MyClass(B* state); 
    virtual ~MyClass(); 

    bool still_valid() const; 
}; 

Каждый экземпляр MyClass добавляет себя refed_list в своем конструкторе и удаляет себя в деструкторе. Если закрытое состояние закрыто, MyClass уведомляется и проверяет refed_list для инкапсулирующего экземпляра и аннулирует его указатель. Это не очень актуально, важно то, что он использует static list, и он обращается к этому списку в конструкторе/деструкторе. Я инициализирую refed_list в файле, где определен MyClass.

Теперь проблема .. Когда моя программа закрывается, среда выполнения в какой-то момент очищает refed_list, а после этого очищает экземпляры MyClass, вызывая их деструкторы. Затем они пытаются получить доступ к refed_list, который уже был очищен. Это приводит к тому, что мои итераторы являются некорректными, и я получаю неопределенное поведение, в этом случае отказ от отладки.

Есть ли способ обойти эту проблему? Я сомневаюсь, что я могу указать, какие объекты порядка в разных единицах компиляции разрушены, но есть ли способ проверить, действительно ли refed_list? На данный момент я проверяю, refed_list.size() == 0 и, похоже, работает, но поведение этого тоже не определено (я думаю?).

+1

Звучит как проблема с одноэлементным циклом. Я думаю, что Скотт Мейерс или Андрей Александреску написал кое-что об управлении ими тремя или так разными способами. Финикс Синглтон может оказаться в вашем переулке. –

+0

И это одна из многих причин, по которым я запрещаю экземпляры глобального класса в моей команде. Потому что порядок уничтожения объекта во время выхода программы является либо недетерминированным, либо просто трудно получить право. В любом случае, можете ли вы просто запустить функцию очистки приложения до того, как WinMain/main вернется?Или еще лучше, если эти деструкторы не делают что-то критически важное, почему бы просто не допустить, чтобы все объекты протекали при завершении работы приложения? – selbie

+0

@selbie Я начинаю думать, что это хорошая идея. Причина, по которой я делаю это, заключается в том, что мне нужно сохранить объект, который может быть признан недействительным до его использования, и я не могу использовать 'shared_ptr', поэтому мне нужно каким-то образом уведомлять объекты, если они действительны или нет. Поэтому мне нужно отслеживать объекты, и поэтому у меня есть статический список. Но да, если я переписал несколько вещей, я могу вручную закрыть его, что должно решить мою проблему. Присоединение к процессам иногда затрудняет работу. – user1520427

ответ

2

Вы всегда можете сделать refed_list указателем, который инициализируется при запуске. Таким образом, он никогда не будет очищен. (И любая память, которую он использует, будет восстановлена ​​ОС при выходе вашего процесса.)

Если это звучит как взломать проблему с более глубоким дизайном, то, вероятно, это так. :)

+0

Да, дизайн не очень хороший, мне пришлось создать метод, который имитирует деструктор, чтобы я мог очистить его до того, как среда выполнения очистит мои глобальные переменные. – user1520427

0

Я не думаю, что это правда:

Когда моя программа закрывает выполнения очищает refed_list в какой-то момент, и после этого она очищает экземпляры MyClass, называя их деструкторы.

Время выполнения, безусловно, очистит список, но не объекты в списке, потому что они указатели. Единственный способ сделать это - использовать класс интеллектуальных указателей, например shared_ptr. Тем не менее, если вы это сделали, объекты будут пытаться получить доступ к списку во время его уничтожения, что я не уверен в неопределенном поведении, но это, безусловно, кажется неустойчивым.

Возможно, вы должны переделать его так, чтобы объектам не нужно ссылаться на список, в котором они хранятся, или, по крайней мере, для этого до список уничтожается (и под этим я имею ввиду перед списком :: ~ список даже называется).

+0

Я не имел в виду, что он освобождает память указателей, которые он хранит, я имел в виду, что объекты, которые наследуют «MyClass», уничтожаются позже, поэтому запускается деструктор MyClass, который пытается для доступа к теперь разрушенному списку. Но да, звучит как редизайн в порядке. – user1520427

Смежные вопросы