1. ホーム
  2. c++

[解決済み] clangの-Wweak-vtablesの意味は何ですか?

2023-07-18 13:45:23

質問

私は基本的にclangの -Wweak-vtables . 以下は、私がこれまでに観察したことです。

ケース1 (警告が表示されます)

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

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

int main(){}

ケース2 (警告を表示しない)

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

int main(){}

ケース3 (警告を表示しない)

class A {
    public:
    virtual ~A();

};

A::~A(){}

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

int main(){}

ケース4 (警告を表示する)

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

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

int main(){}

ケース5 (警告を表示しない)

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

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

int main(){}

ケース6 (警告を表示しない)

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

class B : public A {};

int main(){}

ケース7 (警告が発生しない)

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]

つまり、あるクラスでノンインラインの仮想関数を宣言しなかった場合、そのクラスから派生したクラスが仮想デストラクタを持つ場合に限り、何らかの問題が発生するようです。 派生クラスが仮想デストラクタを持つ場合のみ、何らかの問題が発生するようです。

質問です。

  1. なぜこれが問題なのですか?
  2. なぜ仮想関数を宣言することで解決するのでしょうか?(警告は 定義)
  3. クラスから派生しない場合、なぜ警告が発生しないのですか?
  4. 派生したクラスが仮想デストラクタを持たない場合、なぜ警告が発生しないのですか?

どのように解決するのですか?

もし、あるクラスのすべての virtual メソッドがインラインの場合、コンパイラは vtable の単一の共有コピーを配置する翻訳単位を選択する方法がありません - その代わりに、vtable のコピーはそれを必要とする各オブジェクト ファイルに配置されなければなりません。 多くのプラットフォームでは、リンカは重複する定義を破棄するか、すべての参照を 1 つのコピーにマッピングすることで、これらの複数のコピーを統一することができるため、これは警告にすぎません。

を実装することで virtual 関数を実装すると、コンパイラはその行外メソッドを実装した翻訳ユニットをクラス実装の詳細のための "home"として選択し、vtable の単一の共有コピーを同じ翻訳ユニットに配置することが可能になります。 複数のメソッドが行外である場合、その選択がクラスの宣言によってのみ決定される限り、コンパイラは任意のメソッドを選択することができます。

もしクラスのどのメソッドもオーバーライドしないのであれば virtual キーワードは観察可能な効果を持たないので、コンパイラがそのクラスの vtable を生成する必要はありません。 から派生しない場合は A から派生しない場合、あるいは派生クラスのデストラクタを宣言しない場合は virtual にはオーバーライドされたメソッドはありません。 A であり、したがって A のvtableは省略されます。 もし、追加で行外の virtual メソッドを宣言して警告を抑制し、さらに A の実装では、インラインでない virtual (の実装(とそれに付随するvtableのコピー)はリンクされた翻訳ユニットで提供される必要があり、さもなければvtableが見つからないためリンクに失敗します。