Нужно ли перекомпилировать всю иерархию классов, если в базовый класс в C ++ добавлена ​​виртуальная или не виртуальная функция? - PullRequest
2 голосов
/ 03 июня 2019

Я пытаюсь узнать, насколько чувствительной является vtable класса в C ++, и для этого мне нужно знать, необходима ли перекомпиляция всей иерархии классов (всего, 3 заголовочных файла) для 3 сценариев изменений, которые я список ниже. Во-первых, вот моя иерархия классов:

class A {
   public:
   virtual void method1() = 0;
   virtual void method2() = 0;
   virtual ~A() {} 
};

class B : public A {
    public:
    virtual void method1() {};
    virtual void method2() {};
    virtual ~B() {} 
};

class C : public A {
    public:
    virtual void method1() {};
    virtual void method2() {};
    virtual ~C() {} 
};

Вот мои сценарии:

  1. В базовый класс A добавлен не виртуальный метод:

    void method3() {};
    
  2. В базовый класс A добавлен виртуальный метод с телом:

    virtual void method3() {};
    
  3. В базовый класс A добавлен чисто виртуальный метод:

    virtual void method3() = 0;
    

В сценарии 1 изменения в vtable не вносятся. Требуется ли перекомпиляция B и C?

В сценарии 2 будет ли реконструирована виртуальная таблица для базы A, а следовательно, и для B, и C?

Я знаю, что сценарий 3 заставит классы B и C предоставлять реализации для нового метода. Итак, вся иерархия должна быть перекомпилирована.

Ответы [ 2 ]

7 голосов
/ 03 июня 2019

Правило одного определения C ++ проясняет, что определения сущностей в разных единицах перевода (то есть файлах) должны быть идентичными, если вы собираетесь связать их вместе. Таким образом, если вы измените определение класса вообще, public, private, virtual, non- virtual, независимо от , все единицы перевода, которые используют это определение, должны смотреть на новое определение класса. И это потребует его перекомпиляции.

Неспособность сделать это неверна, но диагностика (ошибка компоновщика) не требуется. Таким образом, ваш проект может показаться, что ссылка просто отлично. Действительно, в некоторых случаях это может сработать. Но нет ничего, что гарантировало бы, в каких случаях они будут работать, а в каких - нет.

2 голосов
/ 03 июня 2019

Независимо от отсутствующего virtual деструктора A у вас есть интерфейс здесь:

class A {
   public:
   virtual void method1() = 0;
   virtual void method2() = 0;
   virtual ~A() {} // This is needed
};

интерфейс - это контракт, который не долженбыть сломанным.

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

class AEx : public A {
public:
   virtual void method3() = 0;
   virtual ~AEx() {}
}; 

В функциях, которые обрабатывают расширения, которые вы можете сделать

void foo(A* pA) {
    AEx pAEx = dynamic_cast<AEx*>(pa);
    if(pAEx) {
       pAEx->method3();
    }
}
...