При каких обстоятельствах vtable указатель может быть нулевым (или 0x1)?

Я в настоящее время отлаживаю crashlog. Катастрофический отказ происходит, потому что vtable указатель (C++), объект является 0x1, в то время как остальная часть объекта, кажется, в порядке насколько я могу сказать от crashlog.

Программа отказывает, когда она пытается назвать виртуальный метод.

Мой вопрос: При каких обстоятельствах vtable указатель может стать пустым? Оператор удаляет, устанавливает vtable указатель в NULL?

Это происходит на OS X с помощью gcc 4.0.1 (сборка Apple Inc. 5493).

7
задан Tobias 15 January 2010 в 14:43
поделиться

5 ответов

может быть топметром памяти - что-то написание над этим VTable по ошибке. Существует почти бесконечное количество способов «достичь» это в C ++. Например, переполнение буфера.

8
ответ дан 6 December 2019 в 06:36
поделиться

Это зависит от времени реализации. Однако было бы вполне безопасно предположить, что после удаления какая-то другая операция может установить пространство памяти на NULL.

Другие возможности включают перезапись памяти каким-то свободным указателем - на самом деле в моем случае это почти всегда это ...

, которые сказали, что вы никогда не должны пытаться использовать объект после удаления.

1
ответ дан 6 December 2019 в 06:36
поделиться

Если разрешить распространение исключения до ОС, ОС зарегистрирует его, а также откроет окно принудительного закрытия, в результате чего приложение будет закрыто. Если его поймать, то можно предотвратить быть закрытой силы приложения.

Если вы хотите, чтобы ваши пользователи имели возможность отправлять вам ошибки, которые они получают, то я бы записал трассировку стека. Затем они могут отправить вам журнал через приложение, например Сборщик журналов .

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

-121--1196105-

Может быть потопом памяти - что-то записывает поверх этого vtable по ошибке. Существует почти бесконечное количество способов «достичь» этого в C++. Например, переполнение буфера.

-121--3783567-

Любое неопределенное поведение может привести к такой ситуации. Например:

  • Ошибки в арифметике указателей или другие ошибки, которые заставляют программу записываться в недопустимую память.
  • Неинициализированные переменные, недопустимые слепки...
  • Полиморфное обращение с массивом может привести к вторичному эффекту.
  • Попытка использовать объект после удаления.

См. также вопросы Какой худший пример неопределенного поведения на самом деле возможен? и О каком общем неопределенном поведении должен знать программист C++? .

Лучше всего использовать ограничения и средства проверки памяти в качестве вспомогательного средства для тяжелой отладки.

8
ответ дан 6 December 2019 в 06:36
поделиться

Очень распространенный случай: попытка вызвать чистый виртуальный Метод от конструктора ...

Конструкторов

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

Почему?

, потому что объект создан снизу вверх. Давайте посмотрим на это.

Инструкции по созданию бетона класса (примерно)

  1. выделите достаточно памяти (конечно), и достаточно памяти для _Vtable тоже (1 функция указатель на виртуальную функцию, обычно в порядке их Объявлен, начиная с самого левого базы)

  2. 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 после того, как он был удален и ранее интерфейс деструктор был вызван? Я не знаю;)

Заключение

никогда не называют виртуальными методами из конструкторов или деструкторов.

Если это не то, что вы остались с повреждением памяти, жесткой удачи;)

6
ответ дан 6 December 2019 в 06:36
поделиться

Мое первое предположение было бы тому, что какой-то код memset () - объект класса.

4
ответ дан 6 December 2019 в 06:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: