Применяется ли фиаско статической инициализации С++ к иерархии классов?

Я получаю сбой (ошибка утверждения отладки: неверный указатель кучи CRT в VC++ 2008) при статической инициализации, и я не уверен, что понимаю, почему.

Я прочитал все о статической инициализации. фиаско в C++ FAQ, и я думал, что понял это - я не понимаю, почему это происходит, или почему это может быть случаем фиаско.

Итак, вот ситуация (большинство нестатических членов опущены для краткости). У меня есть один класс A, определенный в A.h:

class A {
public:
    virtual ~A() { }

    virtual void do_something();
};

Затем у меня есть класс C, у которого есть вложенный класс B, который является подклассом A. C также содержит закрытый статический член типа B:

class C {
public:
    void do_the_C_thing();

private:
    class B : public A {
    public:
        virtual void do_something();
    };

    static B my_personal_B;
};

Наконец, есть файл реализации C, C.cpp, который содержит модуль хранения my_personal_B:

C::B my_personal_B;

C::C() {
}

C::do_the_C_thing() {
    // [...]
    my_personal_B.do_something();
    // [...]
}

void C::B::do_something() {
    // overridden do_something for C's private B class
}

Этот шаблон повторяется для многих классов, каждый из которых имеет вложенный класс, наследуемый от A. Все это работало безупречно в нескольких версиях кода, но в последнее время приложение вылетает с этим конкретным сообщением об ошибке:

Ошибка утверждения отладки!

Программа:
[отредактировано].exe
Файл: f:\dd\vctools\crt_bld\self_x86\crt\src\dbgheap.c
Строка: 1511

> Выражение: _CrtIsValidHeapPointer(pUserData)

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

Это не похоже на статическое фиаско, потому что ничто статическое не ссылается на my_personal_B, ни A, ни B не имеют ничего, кроме конструкторов по умолчанию, и поэтому не могут ссылаться на какой-то другой, еще не инициализированный статический объект. Насколько я понял фиаско, это произошло, когда один статический объект ссылался на другой, еще не инициализированный статический объект.

Тем не менее, если я изменю статический член на метод инициализации при первом использовании, сбой, похоже, исчезнет.

Так вот вопрос, почему этот сбой?


person Brian A. Henning    schedule 28.09.2012    source источник
comment
1. Когда происходит сбой? 2. Что такое стек вызовов?   -  person Dark Falcon    schedule 29.09.2012


Ответы (2)


Класс A имеет виртуальные функции. Это означает, что компилятор создает статический объект, называемый vtable, для хранения указателей на функции-члены. Таким образом, хотя вы не определили никаких статических объектов в классе A, это должен сделать компилятор. Класс B зависит от этой vtable (для виртуального деструктора A, если ничего другого). Очевидно, код инициализации теперь пытается создать объект типа B до того, как будет построена vtable для A.

person Jive Dadson    schedule 28.09.2012
comment
Оооо, хорошо, это имеет смысл и объясняет, почему это даже не появлялось до недавнего времени, когда я добавил объявление виртуального деструктора к A (ранее ни у одного класса в иерархии A не было деструкторов, но это изменилось). (Поскольку я новый постер с низкой репутацией, я не могу проголосовать за ваш ответ... извините) - person Brian A. Henning; 29.09.2012

Чувак, почему ты вообще полагаешься на статическую инициализацию? Вы можете сказать "ошибка проектирования"?

ПРЕДПОЛОЖЕНИЕ:

Если вы не можете изменить дизайн, по крайней мере, обязательно установите флаг «bInitialized», чтобы зависимые клиенты могли проверить, действительно ли ваш объект был инициализирован, и изящно потерпеть неудачу.

ПО МОЕМУ МНЕНИЮ...

person paulsm4    schedule 28.09.2012
comment
Это комментарий, а не ответ. - person Jive Dadson; 29.09.2012