Подтвердить что ты не робот

В чем смысл clang -Wweak-vtables?

В основном я не понимаю clang -Wweak-vtables. Вот что я наблюдал до сих пор:

Случай один: (вызывает предупреждение)

class A {
    public:
    virtual ~A(){}        
};

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

int main(){}

Случай два: (не вызывает предупреждение)

class A {
    public:
    virtual ~A(){}        
};   

int main(){}

Случай три: (не вызывает предупреждение)

class A {
    public:
    virtual ~A();

};

A::~A(){}

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

int main(){}

Случай четыре: (предупреждение триггеров)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}        
};    

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

int main(){}

Случай пять: (не вызывает предупреждение)

class A {
    public:
    virtual ~A(){}
    virtual void fun();      
};    

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

int main(){}

Случай шесть: (не вызывает предупреждение)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {};

int main(){}

Случай семь: (не вызывает предупреждение)

class A {
    public:
    virtual ~A(){}
    virtual void fun(){}
};    

class B : public A {
    public:
    virtual void fun(){}
};

int main(){}

Точное предупреждение

warning: 'A' has no out-of-line virtual method definitions; its vtable 
will be emitted in every translation unit [-Wweak-vtables]

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

Вопросы:

  • Почему это проблема?
  • Почему это фиксируется путем объявления виртуальной функции? (Предупреждение говорит о определения)
  • Почему предупреждение не возникает, если я не получаю от класса?
  • Почему предупреждение не возникает, когда производный класс не имеет виртуального деструктора?
4b9b3361

Ответ 1

Если все методы класса virtual являются встроенными, компилятор не имеет возможности выбрать узел перевода, в котором будет размещена одна общая копия vtable — вместо этого копия vtable должна быть помещена в каждый файл объекта, который в ней нуждается. На многих платформах компоновщик может объединять эти множественные копии либо путем отбрасывания повторяющихся определений, либо путем сопоставления всех ссылок на одну копию, поэтому это только предупреждение.

Реализация функции virtual out-of-line позволяет компилятору выбрать модуль перевода, который реализует этот метод вне линии как "домашний" для деталей реализации класса, и помещает одну общую копию vtable в той же единице перевода. Если несколько методов являются вне очереди, произвольный выбор метода может быть сделан компилятором, если этот выбор определяется только объявлением класса; например, GCC выбирает первый не-встроенный метод в порядке объявления.

Если вы не переопределяете какой-либо метод класса, ключевое слово virtual не имеет наблюдаемого эффекта, поэтому компилятор не должен выделять vtable для класса. Если вы не проистекаете из A, или если вам не удалось объявить деструктор производного класса virtual, в A нет переопределенных методов и, следовательно, A vtable опущен. Если вы объявите дополнительный метод out-of-line virtual для подавления предупреждения, а также выполните что-то, что переопределяет метод в A, реализация не-встроенного virtual (и сопровождающей его копии vtable) должен быть поставлен в связанную единицу перевода, иначе связь не завершится, потому что отсутствует vtable.