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

class Base
{
    public:

    virtual void func1()
    {
        std::cout<<"Base func1"<<std::endl;
    }
   //virtual destructor

};

class Derived : public Base
{
    public:

    virtual void func1()
    {
        std::cout<<"Derived Base func1"<<std::endl;
    }

    virtual void func2()
    {
        std::cout<<"Derived func2"<<std::endl;
    }
};


int main()
{
    Derived *d = new Derived;
    delete d;
}

Я хочу знать, созданы ли два «vptr» для разрешения виртуальных функций, один в «базовом» классе, который будет унаследован в объекте производного класса для func1(), а другой в «производном» объекте для func2().


person Naresh    schedule 03.04.2019    source источник
comment
Вы проверили размеры обоих классов? wandbox.org/permlink/6znTdbKbkv5Yn6IB. Это не даст общего ответа, но может помочь. В связанной реализации в Derived есть только один vptr.   -  person Daniel Langr    schedule 03.04.2019
comment
Я проверил размер объекта базового класса и объекта производного класса, он выглядит одинаково в моей системе 8 байт. Но, по моему мнению, 2 vptr должны быть созданы в объекте производного класса и один в объекте базового класса.   -  person Naresh    schedule 03.04.2019
comment
Почему ты так думаешь? Если GCC создает только один vptr в Derived и мы предполагаем, что он соответствует стандарту, то стандарт не может требовать 2 vptr.   -  person Daniel Langr    schedule 03.04.2019
comment
Кстати, термины vptr, vtable и virtual table вообще не упоминаются в стандарте. Механизм виртуальных таблиц, по-видимому, является проблемой реализации.   -  person Daniel Langr    schedule 03.04.2019
comment
@Нареш должен быть создан почему?   -  person curiousguy    schedule 11.04.2019
comment
@curiousguy Для обработки виртуальной функции производного класса. Но я думаю, что производный класс vptr переопределяет vptr базового класса, и именно поэтому размер объекта производного класса такой же, как у объекта базового класса. Я прав?   -  person Naresh    schedule 11.04.2019
comment
@Naresh макет классов с виртуальными функциями может быть определен разработчиком компилятора в целях двоичной совместимости; это выходит за рамки C++ std.   -  person curiousguy    schedule 11.04.2019


Ответы (2)


На моем GCC:

std::cout << sizeof(void*) << ' ' << sizeof(Derived) << '\n';
// Prints 8 8

Так что одного указателя vtable достаточно. Я ожидаю, что большинство других компиляторов будут вести себя так же.

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

Виртуальные функции, добавленные в Derived, вероятно, просто помещаются в конец Derived vtable после функций, унаследованных от Base.

person HolyBlackCat    schedule 03.04.2019

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

person Red.Wave    schedule 03.04.2019
comment
Я могу себе представить Как будет работать такая реализация? Будет ли это утечка памяти в целом? - person curiousguy; 11.04.2019
comment
@curiousguy Почему это должно протекать? Одной из возможных реализаций является статический ассоциативный массив, в котором хранятся записи пар «объект, тип» (static map, void*, type_info*, rtti;). - person Red.Wave; 11.04.2019
comment
Это приведет к утечке, если объекты не будут уничтожены должным образом. - person curiousguy; 11.04.2019
comment
@curiousguy это не имеет отношения к vt. Механизм управления памятью отвечает за правильное уничтожение и освобождение; является ли тип полиморфным, не имеет значения. - person Red.Wave; 11.04.2019
comment
Как очищается карта? - person curiousguy; 11.04.2019
comment
@curiousguy это всего лишь гипотетическая реализация, но краткий ответ таков: кто бы ни (сгенерированный компилятором код) заполнил карту, он тоже ее очистит. Это не лучший подход, но это одна из возможных реализаций. - person Red.Wave; 11.04.2019
comment
Проблема в том, что все, что не уничтожено должным образом, оставит записи на карте, если нет других механизмов для очистки карты, кроме dtor. - person curiousguy; 11.04.2019
comment
@curiousguy я не вижу, что за афера - person Red.Wave; 11.04.2019
comment
@curiousguy Нет. Что вы имеете в виду под неправильным уничтожением? УБ всегда есть УБ. Если полиморфный объект не уничтожается, ему все равно нужен доступ к его виртуальным методам, в противном случае он не нужен. Я не вижу, что вас смущает. Есть причины, по которым я не видел такой реализации, и я знаю одну. Но это не несуществующая проблема, которую вы ищете - person Red.Wave; 11.04.2019
comment
Чтобы правильно уничтожить объект, вам нужно вызвать его деструктор, неявно или явным вызовом dtor. - person curiousguy; 14.04.2019
comment
@curiousguy как это будет создавать проблемы? Внутренние механизмы ведения хозяйства невидимы для кода. Это то, что должен обрабатывать каждый компилятор (цепочка инструментов). Код программиста должен оставаться вне этого; это должно быть черным ящиком для кода. - person Red.Wave; 14.04.2019
comment
Проблема в том, что пользовательский код не может должным образом уничтожить созданный им объект, как я уже писал. Как будут запускаться внутренние механизмы обслуживания, невидимые для кода, если деструктор не вызывается? - person curiousguy; 14.04.2019
comment
@curiousguy, когда деструктор не вызывается, объект не уничтожается. Какой код вызывает такое поведение? Неправильный выход возвращает память обратно в систему, и утечка ресурсов другого рода практически неизбежна. - person Red.Wave; 14.04.2019
comment
Вы можете просто удалить блок памяти или перезаписать объекты, если вам все равно, запущен ли их dtor. - person curiousguy; 08.05.2019
comment
@curiousguy в любом случае вы должны активировать UB в первую очередь. В противном случае, если код не имеет неправильного формата в соответствии со стандартными соглашениями, ошибки времени выполнения не возникает. - person Red.Wave; 08.05.2019
comment
Как избавиться от памяти или повторно использовать память без вызова dtor каким-либо образом undefined? - person curiousguy; 08.05.2019
comment
@curiousguy Вот что ты должен ответить. - person Red.Wave; 08.05.2019