Почему требуется vptr, если производный класс не переопределяет виртуальную функцию? - PullRequest
8 голосов
/ 02 февраля 2012
class base {
public:
    void virtual fn(int i) {
        cout << "base" << endl;
    }
};

class der : public base{
    public:
    void  fn(char i) {
        cout << "der" << endl;
    }
};

int main() {

    base* p = new der;
    char i = 5;
    p->fn(i);
    cout << sizeof(base);
    return 0;
}

Здесь сигнатура функции fn, определенной в классе base, отличается от сигнатуры функции fn(), определенной в классе der, хотя имя функции совпадает.Поэтому функция, определенная в der class, скрывает base class function fn().Так что класс der версия fn не может быть вызвана вызовом p->fn(i);Это хорошо.

Тогда я имею в виду, почему sizeof class base или der равен 4, если не используется Указатель VTABLE ?Каково здесь требование указатель VTABLE ?

Ответы [ 4 ]

6 голосов
/ 02 февраля 2012

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

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

Два класса Base и Derived могут находиться в разных единицах перевода, и компилятор при компиляции базового класса не будет знать, будет ли метод переопределен или нет. Таким образом, если он находит ключевое слово virtual, он генерирует vtable.

1 голос
/ 23 апреля 2016

Компилятор не может оптимизировать vtable переменную-член вне базового класса, поскольку в этом же или другом проекте может быть другой исходный файл, который будет содержать следующее:

struct ived : base {
    ived() : p(new char[BIG_DATA_SIZE]) {}
    virtual ~ived();
    virtual void fn(int);
private:
    char* p;
};

Деструктори fn может быть реализован где-то еще:

ived::~ived() { delete[] p; }

void ived::fn(int) {
    cout << "ived" << endl;
}

И где-то в другом месте может быть код, подобный этому:

base* object = new ived;
ived->fn(0);
delete object;
cout << sizeof(base) << endl;

Итак, возникнут две проблемы: виртуальная функцияived::fn не вызывается, виртуальный деструктор не вызывается, поэтому BIG_DATA_SIZE не удаляется.В противном случае sizeof(base) здесь будет другим.Вот почему компиляторы всегда генерируют vtable для любого класса с виртуальной функцией-членом или виртуальным базовым классом.

Что касается вызова деструкторов в производных классах, его необходимо рассматривать как обязательный: если у вас есть какой-либо класс с какой-либо виртуальной функцией, этот класс также должен объявить виртуальный деструктор.

1 голос
/ 02 февраля 2012

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

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

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

0 голосов
/ 02 февраля 2012

Наследование - это is-a отношение. der is-a base. base имеет размер 4, der будет иметь как минимум размер 4. vftableptr является членом base, он будет членом der.

base имеет виртуальный метод, поэтому у него будет указатель на виртуальную таблицу, независимо от того, используете ли вы его или нет.

...