Я в настоящее время отлаживаю crashlog. Катастрофический отказ происходит, потому что vtable указатель (C++), объект является 0x1, в то время как остальная часть объекта, кажется, в порядке насколько я могу сказать от crashlog.
Программа отказывает, когда она пытается назвать виртуальный метод.
Мой вопрос: При каких обстоятельствах vtable указатель может стать пустым? Оператор удаляет, устанавливает vtable указатель в NULL?
Это происходит на OS X с помощью gcc 4.0.1 (сборка Apple Inc. 5493).
может быть топметром памяти - что-то написание над этим VTable
по ошибке. Существует почти бесконечное количество способов «достичь» это в C ++. Например, переполнение буфера.
Это зависит от времени реализации. Однако было бы вполне безопасно предположить, что после удаления какая-то другая операция может установить пространство памяти на NULL.
Другие возможности включают перезапись памяти каким-то свободным указателем - на самом деле в моем случае это почти всегда это ...
, которые сказали, что вы никогда не должны пытаться использовать объект после удаления.
Если разрешить распространение исключения до ОС, ОС зарегистрирует его, а также откроет окно принудительного закрытия, в результате чего приложение будет закрыто. Если его поймать, то можно предотвратить быть закрытой силы приложения.
Если вы хотите, чтобы ваши пользователи имели возможность отправлять вам ошибки, которые они получают, то я бы записал трассировку стека. Затем они могут отправить вам журнал через приложение, например Сборщик журналов .
Если вы хотите избежать возможности предоставления пользователям информации трассировки стека, то поймите исключение и не регистрируйте его в журнале.
-121--1196105- Может быть потопом памяти - что-то записывает поверх этого vtable
по ошибке. Существует почти бесконечное количество способов «достичь» этого в C++. Например, переполнение буфера.
Любое неопределенное поведение может привести к такой ситуации. Например:
См. также вопросы Какой худший пример неопределенного поведения на самом деле возможен? и О каком общем неопределенном поведении должен знать программист C++? .
Лучше всего использовать ограничения и средства проверки памяти в качестве вспомогательного средства для тяжелой отладки.
Очень распространенный случай: попытка вызвать чистый виртуальный Метод от конструктора ...
Конструкторов
struct Interface
{
Interface();
virtual void logInit() const = 0;
};
struct Concrete: Interface()
{
virtual void logInit() const { std::cout << "Concrete" << std::endl; }
};
Теперь предположим, что следующее внедрение интерфейса ()
Interface::Interface() {}
, то все хорошо:
Concrete myConcrete;
myConcrete.pure(); // outputs "Concrete"
Это такая боль, чтобы позвонить чистые после конструктора, это Было бы лучше фактифицировать код правильно?
Interface::Interface() { this->logInit(); } // DON'T DO THAT, REALLY ;)
Тогда мы можем сделать это в одной строке !!
Concrete myConcrete; // CRASHES VIOLENTLY
Почему?
, потому что объект создан снизу вверх. Давайте посмотрим на это.
Инструкции по созданию бетона
класса (примерно)
выделите достаточно памяти (конечно), и достаточно памяти для _Vtable тоже (1 функция указатель на виртуальную функцию, обычно в порядке их Объявлен, начиная с самого левого базы)
Call Бетон
Конструктор (код, который вы не видите)
A> Call Интерфейс
Конструктор, который инициализирует _VTable с его Указатели
B> Вызов Интерфейс
Тело конструктора (вы написали это)
C> Переопределить указатели в _VTable для тех методов бетонных переопределений
D> Вызов Бетон
Тело конструктора (вы написали это)
Так в чем проблема? Ну, посмотрите на б>
и с>
порядок;)
При вызове виртуальный
метод из конструктора, он не делает то, Ты надеешься на. Он идет к _VTable, чтобы найти указатель, но _VTable
еще не полностью инициализирован. Таким образом, для всех, что имеет значение, эффект:
D() { this->call(); }
на самом деле:
D() { this->D::call(); }
при вызове виртуального метода из конструктора вы не заполните полный динамический тип объекта, который указывается, у вас есть статический тип нынешнего конструктора вызывается.
В моем интерфейсе
/ бетон
, это означает, что это означает интерфейс
типа , а метод является виртуальным чистым, поэтому _VTable не содержит реального указателя ( 0x0 или 0x01 Например, если ваш компилятор достаточно дружелюбный для настройки значений отладки, чтобы помочь вам там).
деструкторы
совпадают, давайте рассмотрим дело деструктора;)
struct Interface { ~Interface(); virtual void logClose() const = 0; }
Interface::~Interface() { this->logClose(); }
struct Concrete { ~Concrete(); virtual void logClose() const; char* m_data; }
Concrete::~Concrete() { delete[] m_data; } // It's all about being clean
void Concrete::logClose()
{
std::cout << "Concrete refering to " << m_data << std::endl;
}
Так что происходит при уничтожении? Ну, _vtable работает красиво, и вызывается реальный тип выполнения ... Что это значит здесь, однако, не определено поведение, потому что кто знает, что случилось с m_data
после того, как он был удален и ранее интерфейс
деструктор был вызван? Я не знаю;)
Заключение
никогда не называют виртуальными методами из конструкторов или деструкторов.
Если это не то, что вы остались с повреждением памяти, жесткой удачи;)
Мое первое предположение было бы тому, что какой-то код memset ()
- объект класса.