Перегрузка оператора delete в базовом классе

Из стандарта C++ (ISO/IEC 14882:2003(E)), §12.5.4, о перегрузке operator delete:

Если выражение delete начинается с унарного оператора::, имя функции освобождения ищется в глобальной области видимости. В противном случае, если выражение delete используется для освобождения объекта класса, статический тип которого имеет виртуальный деструктор, функция освобождения является функцией, найденной поиском в определении виртуального деструктора динамического типа (12.4). В противном случае, если выражение delete используется для освобождения объекта класса T или его массива, статический и динамический типы объекта должны быть идентичными, а имя функции освобождения ищется в области видимости T. Если этот поиск не удается найти имя, имя ищется в глобальной области видимости. Если результат поиска является неоднозначным или недоступным, или если поиск выбирает функцию освобождения места размещения, программа некорректна.

§12.5.7 также интересен:

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

struct B {
    virtual ˜B();
    void operator delete(void*, size_t);
};
struct D : B {
    void operator delete(void*);
};
void f()
{
    B* bp = new D;
    delete bp; // uses D::operator delete(void*)
}

Здесь хранилище для объекта, не являющегося массивом класса D, освобождается оператором D:: delete() из-за виртуального деструктора.]

Прочитав это, мне интересно...

  • Эта часть стандарта полностью поддерживается всеми основными компиляторами C++ (MSVC++, GCC)?
  • Если так, то как они это сделали? Скрытая виртуальная функция? "Специальный" виртуальный вызов деструктора? RTTI?
  • Используя пример из стандарта: могут ли быть проблемы, если f() и оператор удаления D:: () определены в отдельных файлах EXE/DLL/DSO? (Конечно, при условии, что все скомпилировано с использованием одного и того же компилятора)

§5.3.5.5 также может иметь отношение к:

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

2 ответа

Решение

Я не знаю много о VC++ ABI, но Itanium ABI хорошо документирован.

Глядя на схему искажения имени, можно увидеть:

<ctor-dtor-name> ::= C1     # complete object constructor
                 ::= C2     # base object constructor
                 ::= C3     # complete object allocating constructor
                 ::= D0     # deleting destructor
                 ::= D1     # complete object destructor
                 ::= D2     # base object destructor

Представляет интерес: D0 # deleting destructorЭто означает, что даже если delete не является виртуальным, так как он вызывается из виртуального деструктора, его можно считать виртуальным для всех эффектов и целей.

После копания в ассемблерном коде в GCC 4.8.

GCC сгенерирует две части кода (для класса, деструктор которого является виртуальным):

One is assembly snippet#1 for {Destructor + Dealloc}
The other is assembly snippet#2 for {Destructor only}

А для класса, деструктор которого не является виртуальным, инструкция функции освобождения вызова сгенерирует в точке, где вы вызываете delete.

(Следующее обсуждение предполагает, что деструктор является виртуальным). Так что для следующего кода:

delete C   // This will be translate as call snippet#1 for the correct dynamic type

И если вы код следующий:

p->C::~C()  // this will be translate to call snippet#2

Таким образом, функция deallocate связывается вместе с виртуальным деструктором. Так что я думаю, что это ответит на ваш вопрос о том, как функция deallocate реализована как виртуальная, так и статическая.

Другие вопросы по тегам