vtableへの未定義の参照


357

C ++プログラムをビルドすると、エラーメッセージが表示される

'vtableへの未定義の参照...

この問題の原因は何ですか?どうすれば修正できますか?


たまたま、次のコード(問題のクラスはCGameModuleです)でエラーが発生し、私の人生では問題が何であるかを理解できません。最初は、仮想関数に体を与えるのを忘れることに関係があると思っていましたが、私の知る限り、すべてがここにあります。継承チェーンは少し長いですが、関連するソースコードを次に示します。他にどのような情報を提供すべきかわかりません。

注:コンストラクターがこのエラーが発生しているようです。

私のコード:

class CGameModule : public CDasherModule {
 public:
  CGameModule(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName)
  : CDasherModule(pEventHandler, pSettingsStore, iID, 0, szName)
  { 
      g_pLogger->Log("Inside game module constructor");   
      m_pInterface = pInterface; 
  }

  virtual ~CGameModule() {};

  std::string GetTypedTarget();

  std::string GetUntypedTarget();

  bool DecorateView(CDasherView *pView) {
      //g_pLogger->Log("Decorating the view");
      return false;
  }

  void SetDasherModel(CDasherModel *pModel) { m_pModel = pModel; }


  virtual void HandleEvent(Dasher::CEvent *pEvent); 

 private:



  CDasherNode *pLastTypedNode;


  CDasherNode *pNextTargetNode;


  std::string m_sTargetString;


  size_t m_stCurrentStringPos;


  CDasherModel *m_pModel;


  CDasherInterfaceBase *m_pInterface;
};

継承元...

class CDasherModule;
typedef std::vector<CDasherModule*>::size_type ModuleID_t;

/// \ingroup Core
/// @{
class CDasherModule : public Dasher::CDasherComponent {
 public:
  CDasherModule(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, ModuleID_t iID, int iType, const char *szName);

  virtual ModuleID_t GetID();
  virtual void SetID(ModuleID_t);
  virtual int GetType();
  virtual const char *GetName();

  virtual bool GetSettings(SModuleSettings **pSettings, int *iCount) {
    return false;
  };

 private:
  ModuleID_t m_iID;
  int m_iType;
  const char *m_szName;
};

から継承しています...

namespace Dasher {
  class CEvent;
  class CEventHandler;
  class CDasherComponent;
};

/// \ingroup Core
/// @{
class Dasher::CDasherComponent {
 public:
  CDasherComponent(Dasher::CEventHandler* pEventHandler, CSettingsStore* pSettingsStore);
  virtual ~CDasherComponent();

  void InsertEvent(Dasher::CEvent * pEvent);
  virtual void HandleEvent(Dasher::CEvent * pEvent) {};

  bool GetBoolParameter(int iParameter) const;
  void SetBoolParameter(int iParameter, bool bValue) const;

  long GetLongParameter(int iParameter) const;
  void SetLongParameter(int iParameter, long lValue) const;

  std::string GetStringParameter(int iParameter) const;
  void        SetStringParameter(int iParameter, const std::string & sValue) const;

  ParameterType   GetParameterType(int iParameter) const;
  std::string     GetParameterName(int iParameter) const;

 protected:
  Dasher::CEventHandler *m_pEventHandler;
  CSettingsStore *m_pSettingsStore;
};
/// @}


#endif

「vtableへの未定義の参照...」をスローしている関数はどれですか。
J. Polfer、2010年

3
エラーメッセージで関数が指定されていることを完全に見逃しました。たまたまコンストラクタなので、クラス名を見て接続しませんでした。したがって、コンストラクターはこれをスローしています。その詳細を元の投稿に追加します。
RyanG 2010年

3
あなたが(例えば重要な変更行った後で、プロジェクトファイルを再構築していない場合はqmake -project、その後とのqmake新しいを生成する)Makefile、Qtのを使用してエラーの可能性のあるソースです。
デビッドC.ランキン

@ DavidC.Rankin、別のQt関連の問題は、でファイルQ_OBJECTが外部にコピーされるが、.proファイルの一部ではない場合、正常にコンパイルされますが、リンクされないことです。そのためには、その.h/.cppファイルを.proファイルに追加する必要がありますqmake
iammilind

回答:


420

GCCのFAQには、その上にエントリがあります。

解決策は、純粋ではないすべての仮想メソッドが定義されていることを確認することです。デストラクターは、pure-virtual [class.dtor] / 7と宣言されていても定義する必要があることに注意してください。


17
nm -C CGameModule.o | grep CGameModule::クラスの実装全体が論理オブジェクトファイルに入ると想定して、定義されているメソッドをリストします。これを仮想として定義されているものと比較して、見逃したものを把握できます。
Troy Daniels

132
FFS、コンパイラがそれをチェックしてエラーメッセージを出力しないのはなぜですか?
Lenar Hoyt 2014

20
明らかに、これはコンパイラーではなく、リンカーによってのみ発見できます。
Xoph

2
私の場合、デストラクタの実装がない抽象クラスがありました。空の実装
〜MyClass

1
リンクしようとしているオブジェクトがアーカイブ(libxyz.aファイル)にない場合、次のようなエラーが発生する可能性があります。`vtablefor objfilename 'への未定義の参照
Kemin Zhou

162

価値があることについては、仮想デストラクタでボディを忘れると、次のようになります。

「vYour for CYourClass」への未定義の参照。

エラーメッセージは紛らわしいため、メモを追加しています。(これはgccバージョン4.6.3でした。)


23
空の仮想デストラクタの本体を定義ファイル(* .cc)に明示的に置く必要がありました。それをヘッダーに入れてもエラーが発生しました。
PopcornKing 2014年

4
仮想デストラクタを実装ファイルに追加すると、gccが実際のエラーを通知したことに注意してください。これは、別の関数での本体の欠落でした。
moodboom 2015

1
@PopcornKing私は同じ問題を見ました。~Destructor = default;ヘッダーファイルで定義することも役に立ちませんでした。gccに対して報告された文書化されたバグはありますか?
RD

これは別の問題である可能性がありますが、私の問題は非仮想デストラクタの実装がなかっただけです(一意の/共有ポインタに切り替えてソースファイルから削除しましたが、ヘッダーに「実装」がありませんでした) )
16

これで問題が解決し、仮想デストラクタに空の{}ボディを追加するとエラーが回避されました。
Bogdan Ionitza

56

それで、私は問題を理解しました、そしてそれは悪いロジックの組み合わせであり、automake / autotoolsの世界に完全に精通していませんでした。Makefile.amテンプレートに正しいファイルを追加していましたが、ビルドプロセスのどのステップで実際にmakefile自体が作成されたかはわかりませんでした。それで、私は私の新しいファイルについて全く何も知らない古いmakefileでコンパイルしていました。

回答とGCC FAQへのリンクをありがとう。本当の理由で発生するこの問題を回避するために、必ずお読みください。


43
つまり、.cppはビルドに含まれていませんでした。エラーメッセージは本当に誤解を招くものです。
Offirmo 2013年

67
Qtユーザーの場合:ヘッダーのmocを忘れた場合も、同じエラーが発生する可能性があります。
Chris Morlier、2013

8
アレクサンドル・ハメズの答えを受け入れるべきだと思います。このエラーを検索する人々は、おそらくあなたの代わりに彼の解決策を必要とするでしょう。
Tim

13
-1これで問題が解決する可能性がありますが、元の質問に対する回答ではありません。正解は、必要なシンボルを含むオブジェクトファイルを提供しなかったことです。それらを提供できなかった理由は別の話です。
Walter

12
@ウォルター:実際、これは私が探していた正確な答えでした。他のものは明白であり、したがって役に立たない。
Edgar Bonet 2014年

50

Qtを使用している場合は、qmakeを再実行してください。このエラーがウィジェットのクラスにある場合、qmakeは、UIクラスvtableを再生成する必要があることに気付かなかった可能性があります。これで問題が解決しました。


2
私はそれがうまくいくビルドでフォルダ全体を削除しました。
トマーシュZato -復活モニカ

これはに固有のものqmakeではなく、と同じcmakeでした。問題の一部は、両方のツールにヘッダーファイルに関する少しの問題があり、必要なときに常に再構築がトリガーされるとは限らないことです。
MSalters 2016

2
「再構築」はqmakeを自動的に再実行すると考えましたが、明らかにそうではありません。私はあなたの提案で「Run qmake」を実行し、次に「Rebuild」を実行すると問題が解決しました。
やの

45

次の状況でも、vtableへの未定義の参照が発生することがあります。ちょうどこれを試してください:

クラスAに含まれるもの:

virtual void functionA(parameters)=0; 
virtual void functionB(parameters);

クラスBに含まれるもの:

  1. 上記のfunctionAの定義。
  2. 上記のfunctionBの定義。

クラスCの内容:クラスAから派生させるクラスCを記述しています。

コンパイルしようとすると、クラスCのvtableへの未定義の参照がエラーとして表示されます。

理由:

functionAは純粋な仮想として定義され、その定義はクラスBで提供functionBされます。は仮想(非純粋仮想)として定義される ため、クラスA自体でその定義を見つけようとしますが、クラスBで定義を提供しました。

解決:

  1. 関数Bを純粋な仮想として作成します(そのような要件がある場合) virtual void functionB(parameters) =0; (これは動作し、テスト済みです)。
  2. クラスA自体にfunctionBの定義を提供し、それをvirtualのままにします。(私がこれを試さなかったのでうまくいくと思います)

@ ilya1725 提案された編集は、書式設定などを修正するだけではなく、回答も変更します。たとえば、クラスCはAではなくBから派生し、2番目のソリューションを変更するとします。これは答えを大きく変えます。このような場合は、代わりに作者にコメントを残してください。ありがとうございました!
ファビオはモニカを復活させる2017

@FabioTuratiそれからclassCが継承するクラスは何ですか?文は明確ではありません。また、「クラスCの内容:」の意味は何ですか?
ilya1725

@ ilya1725この答えはあまり明確ではありません。私はそれを編集して改善することに反対していません。私が言っているのは、あなたの編集が答えの意味を変えるということです、そしてそれはあまりにも劇的な変化です。うまくいけば、作者が介入して、彼が何を意味するのかを明らかにします(ただし、長い間活動していませんでした)。
Fabioはモニカを

ありがとう!私の場合、階層には2つのクラスしかありませんでした。クラスAは純粋な仮想メソッドを宣言しました。クラスBの宣言は、このメソッドをオーバーライドすることを述べていますが、オーバーライドされたメソッドの定義はまだ記述していません。
Nick Desaulniers

44

私のcppファイルがmakefileになかったため、単にこのエラーが発生しました。


確かに、関わってundefined reference to {function/class/struct}いるvirtualものがあるとメッセージがいつもと少し違うようです。私を捨てた。
キースM

30

とはvtable

それを修正しようとする前に、エラーメッセージが何を話しているのかを知ることは有用かもしれません。高レベルから始めて、いくつかの詳細に取り掛かります。これにより、vtableの理解に慣れたら、先にスキップできます。…そして今、先を飛んでいる多くの人々がいます。:)周りに固執する人のために:

vtableは基本的に多態性の最も一般的な実装です にC ++です。vtableを使用する場合、すべてのポリモーフィッククラスには、プログラムのどこかにvtableがあります。staticクラスの(非表示の)データメンバーと考えることができます。ポリモーフィッククラスのすべてのオブジェクトは、その最も派生したクラスのvtableに関連付けられています。この関連付けをチェックすることにより、プログラムはその多態性の魔法を働かせることができます。重要な警告: vtableは実装の詳細です。ほとんど(すべて?)のC ++コンパイラーがvtableを使用してポリモーフィックな動作を実装していますが、C ++標準では義務付けられていません。私が提示する詳細は、典型的または合理的なアプローチです。コンパイラはこれから逸脱することができます!

各ポリモーフィックオブジェクトには、オブジェクトの最も派生したクラスのvtableへの(非表示の)ポインターがあります(より複雑な場合は、複数のポインターである可能性があります)。ポインタを見ると、プログラムはオブジェクトの「実際の」タイプが何であるかを知ることができます(構築中を除きますが、その特別なケースはスキップしましょう)。たとえば、タイプのオブジェクトがAのvtableを指していない場合、Aそのオブジェクトは実際にはから派生しAた何かのサブオブジェクトです。

「vtable」という名前は「 v irtual function table」にます。これは、(仮想)関数へのポインタを格納するテーブルです。コンパイラーは、テーブルのレイアウト方法に関する規則を選択します。簡単なアプローチは、仮想関数をクラス定義内で宣言された順序で実行することです。仮想関数が呼び出されると、プログラムはオブジェクトのポインターをvtableにたどり、目的の関数に関連付けられているエントリに移動し、格納された関数ポインターを使用して正しい関数を呼び出します。この作業を行うためのさまざまなトリックがありますが、ここでは説明しません。

どこで/いつvtable生成されますか?

vtableは、コンパイラーによって自動的に生成されます(「emitted」と呼ばれることもあります)。コンパイラは、ポリモーフィッククラス定義を参照するすべての翻訳単位でvtableを発行できますが、通常、これは不必要なやりすぎです。代替( gcc使用され、おそらく他のユーザーでも使用されます)は、クラスの静的データメンバーを配置する単一のソースファイルを選択する方法と同様に、vtableを配置する単一の翻訳単位を選択することです。この選択プロセスで変換単位を選択できない場合、vtableは未定義の参照になります。したがって、そのエラーのメッセージは明らかに明確ではありません。

同様に、選択プロセスで翻訳単位が選択されても、そのオブジェクトファイルがリンカーに提供されない場合、vtableは未定義の参照になります。残念ながら、この場合のエラーメッセージは、選択プロセスが失敗した場合よりも明確ではありません。(この可能性について述べた回答者に感謝します。それ以外の場合はおそらく忘れていたでしょう。)

gccが使用する選択プロセスは、その実装に必要な各クラスに(単一の)ソースファイルを割り当てるという伝統から始める場合に意味があります。そのソースファイルをコンパイルするときにvtableを出力すると便利です。それを私たちの目標としましょう。ただし、この伝統に従っていない場合でも、選択プロセスは機能する必要があります。したがって、クラス全体の実装を探す代わりに、クラスの特定のメンバーの実装を探しましょう。伝統が守られている場合 -そしてそのメンバーが実際に実装されている場合、目標は達成されます。

gcc(および場合によっては他のコンパイラー)によって選択されたメンバーは、純粋な仮想ではない最初の非インライン仮想関数です。他のメンバー関数の前にコンストラクターとデストラクターを宣言する群衆の一部である場合、そのデストラクターが選択される可能性が高くなります。(デストラクタを仮想化することを覚えていますか?)例外があります。最も一般的な例外は、デストラクタにインライン定義が提供されている場合、およびデフォルトのデストラクタが要求されている場合( "= default ")です。

鋭敏な人は、ポリモーフィッククラスがすべての仮想関数のインライン定義を提供できることに気づくかもしれません。選択プロセスが失敗するのではないですか?古いコンパイラではそうなります。最新のコンパイラーがこの状況に対処したことを読みましたが、関連するバージョン番号はわかりません。私はこれを調べてみることができますが、その周りにコーディングするか、コンパイラーが文句を言うのを待つ方が簡単です。

要約すると、「vtableへの未定義の参照」エラーの3つの主要な原因があります。

  1. メンバー関数にその定義がありません。
  2. オブジェクトファイルがリンクされていません。
  3. すべての仮想関数にはインライン定義があります。

これらの原因は、それ自体ではエラーを引き起こすには不十分です。むしろ、これらはエラーを解決するために対処するものです。これらの状況の1つを意図的に作成すると間違いなくこのエラーが発生することを期待しないでください。他の要件があります。これらの状況を解決すると、このエラーが解決することを期待してください。

(OK、この質問が尋ねられたとき、3番目で十分だったかもしれません。)

エラーを修正する方法は?

先にスキップして人々を歓迎します!:)

  1. クラス定義を見てください。純粋な仮想( " = 0" ではない)ではなく、定義が(" " ではない)最初の非インライン仮想関数を見つける= default "。
    • そのような関数がない場合は、クラスを変更してみてください。 (エラーは解決される可能性があります。)
    • 注意事項については、フィリップトーマスの回答も参照してください。
  2. その関数の定義を見つけます。足りない場合は追加してください!(エラーは解決される可能性があります。)
  3. リンクコマンドを確認してください。その関数の定義を含むオブジェクトファイルについて言及されていない場合は、修正してください!(エラーは解決される可能性があります。)
  4. エラーが解決されるまで、仮想関数ごとにステップ2と3を繰り返し、次に非仮想関数ごとに繰り返します。それでも解決しない場合は、静的データメンバーごとに繰り返します。


行うべきことの詳細は異なる場合があり、別の質問に分かれる場合があります(未定義の参照/未解決の外部シンボルエラーとは何か、それをどのように修正するかなど)。)。ただし、新しいプログラマーを困惑させる可能性がある特定のケースで何をすべきかの例を提供します。

ステップ1では、特定のタイプの関数を持つようにクラスを変更することについて説明しています。その機能の説明があなたの頭を超えた場合、私が対処しようとしている状況にあるかもしれません。これは目標を達成するための方法であることを覚えておいてください。それが唯一の方法ではなく、特定の状況でより良い方法が簡単に存在する可能性があります。クラスと呼びましょうA。あなたのデストラクタは(クラス定義で)どちらかとして宣言されていますか

virtual ~A() = default;

または

virtual ~A() {}

?その場合、2つのステップでデストラクタが必要な関数のタイプに変わります。まず、その行を

virtual ~A();

次に、プロジェクトの一部であるソースファイル(できれば、クラスの実装があるファイル)に次の行を追加します。

A::~A() {}

これにより、(仮想)デストラクタが非インラインになり、コンパイラによって生成されなくなります。(関数の定義にヘッダーコメントを追加するなど、コードの書式設定スタイルに合わせて自由に変更してください。)


おお、ブラボー!非常に詳細で範囲の広い説明について。
デビッドC.ランキン

これを読むには、ずっと下にスクロールする必要がありました。優れた説明に賛成投票してください!
トーマス

24

ここでは、さまざまな答えで多くの推測が行われています。以下に、このエラーを再現するかなり最小限のコードを示し、それが発生する理由を説明します。

このエラーを再現するためのかなり最小限のコード

IBase.hpp

#pragma once

class IBase {
    public:
        virtual void action() = 0;
};

Derived.hpp

#pragma once

#include "IBase.hpp"

class Derived : public IBase {
    public:
        Derived(int a);
        void action() override;
};

Derived.cpp

#include "Derived.hpp"
Derived::Derived(int a) { }
void Derived::action() {}

myclass.cpp

#include <memory>
#include "Derived.hpp"

class MyClass {

    public:
        MyClass(std::shared_ptr<Derived> newInstance) : instance(newInstance) {

        }

        void doSomething() {
            instance->action();
        }

    private:
        std::shared_ptr<Derived> instance;
};

int main(int argc, char** argv) {
    Derived myInstance(5);
    MyClass c(std::make_shared<Derived>(myInstance));
    c.doSomething();
    return 0;
}

これは、次のようにGCCを使用してコンパイルできます。

g++ -std=c++11 -o a.out myclass.cpp Derived.cpp

= 0IBase.hppを削除することでエラーを再現できます 。私はこのエラーを受け取ります:

~/.../catkin_ws$ g++ -std=c++11 -o /tmp/m.out /tmp/myclass.cpp /tmp/Derived.cpp
/tmp/cclLscB9.o: In function `IBase::IBase(IBase const&)':
myclass.cpp:(.text._ZN5IBaseC2ERKS_[_ZN5IBaseC5ERKS_]+0x13): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o: In function `IBase::IBase()':
Derived.cpp:(.text._ZN5IBaseC2Ev[_ZN5IBaseC5Ev]+0xf): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o:(.rodata._ZTI7Derived[_ZTI7Derived]+0x10): undefined reference to `typeinfo for IBase'
collect2: error: ld returned 1 exit status

説明

上記のコードでは、コンパイルを成功させるために、仮想デストラクタ、コンストラクタ、またはその他の追加ファイルは必要ありません(ただし、それらは必要です)。

このエラーを理解する方法は次のとおりです。リンカーはIBaseのコンストラクターを探しています。これは、Derivedのコンストラクターで必要になります。ただし、DerivedはIBaseのメソッドをオーバーライドするため、IBaseを参照するvtableがアタッチされています。リンカーが「IBaseのvtableへの未定義の参照」と言う場合、それは基本的にDerivedがIBaseへのvtable参照を持っていることを意味しますが、検索するIBaseのコンパイル済みオブジェクトコードを見つけることができません。つまり、IBaseクラスには実装のない宣言があるということです。これは、IBaseのメソッドが仮想として宣言されていることを意味しますが、純粋な仮想としてマークするのを忘れているか、その定義を提供しています。

パーティングチップ

他のすべてが失敗した場合、このエラーをデバッグする1つの方法は、コンパイルを実行する最小限のプログラムをビルドし、それを変更し続けることで、目的の状態になるようにすることです。その間、いつコンパイルが失敗し始めたかを確認します。

ROSとCatkinビルドシステムに関する注意

catkinビルドシステムを使用してROSで上記のクラスのセットをコンパイルしていた場合、CMakeLists.txtに次の行が必要になります。

add_executable(myclass src/myclass.cpp src/Derived.cpp)
add_dependencies(myclass theseus_myclass_cpp)
target_link_libraries(myclass ${catkin_LIBRARIES})

最初の行は基本的に、myclassという名前の実行可能ファイルを作成することを示しています。これをビルドするコードは、次のファイルで見つけることができます。これらのファイルの1つにはmain()が必要です。CMakeLists.txtのどこにも.hppファイルを指定する必要がないことに注意してください。また、ライブラリとしてDerived.cppを指定する必要はありません。


18

確認できるこのエラーの別の原因に遭遇しました。

基本クラスは、純粋な仮想関数を次のように定義しました。

virtual int foo(int x = 0);

そしてサブクラスは

int foo(int x) override;

問題は"=0"、括弧の外にあるはずのタイプミスでした。

virtual int foo(int x) = 0;

したがって、ここまで下にスクロールしている場合、おそらく答えが見つからなかった可能性があります。これは確認する必要があるものです。


12

これは、定義を持つオブジェクトファイルへのリンクを忘れた場合、非常に簡単に発生します。


1
回答と可能な修正にいくつか説明を追加してください。
Mohit Jain

1
リンクを忘れることは、ビルド命令に追加することを忘れることを含みます。私の場合、cppファイルをソースリストに追加するのを忘れた(私のCMakeLists.txtにありますが、同じことが.proファイルなどの他のビルドシステムでも発生する可能性があります)以外、すべてのcppファイルは完全に「定義」されています。その結果、すべてがコンパイルされ、リンク時にエラーが発生しました...
sage

@Mohit Jainオブジェクトファイルにリンクする方法は、環境のセットアップとツールによって異なります。残念ながら、ある人のための特定の修正は別の人のために異なる可能性があります(たとえば、CMakeと独自のツールとIDEとなど)
Hazok

11

GNU C ++コンパイラは、 vtable、オブジェクトの仮想関数の定義が複数のコンパイルユニットに分散している場合に必要があります(たとえば、オブジェクトの仮想関数の定義の一部は、他の.cppファイルにあります。 cppファイルなど)。

コンパイラは、 vtable、最初に宣言された仮想関数が定義されている場所と同じ場所にます。

何らかの理由で、オブジェクトで宣言された最初の仮想関数の定義を提供し忘れた場合(またはリンクフェーズでコンパイル済みオブジェクトを追加し忘れた場合)、このエラーが発生します。

副作用として、この特定の仮想関数についてのみ、関数fooがないなどの従来のリンカーエラーが発生しないことに注意してください。


8

クロスポストではなく。継承を扱っている場合、2番目のGoogleヒットは私が見逃していたものでした。すべての仮想メソッドを定義する必要があります。

といった:

virtual void fooBar() = 0;

詳細については、Answare C ++ Undefined Reference to vtable and inheritanceを参照してください。すでに上記で説明されていることに気づきましたが、それは誰かを助けるかもしれません。


8

さて、これに対する解決策は、あなたが定義を見逃したかもしれないということです。vtableコンパイラエラーを回避するには、以下の例を参照してください。

// In the CGameModule.h

class CGameModule
{
public:
    CGameModule();
    ~CGameModule();

    virtual void init();
};

// In the CGameModule.cpp

#include "CGameModule.h"

CGameModule::CGameModule()
{

}

CGameModule::~CGameModule()
{

}

void CGameModule::init()    // Add the definition
{

}

7
  • CDasherComponentデストラクタ用のボディがあることを確認しますか?これは間違いなくここにありません。問題は、それが.ccファイルにあるかどうかです。
  • スタイルの観点から、CDasherModuleデストラクタを明示的に定義する必要がありますvirtual
  • 最後にCGameModuleエキストラがあるように見えます}(後}; // for the class)ます。
  • されるCGameModule定義ライブラリにリンクされているCDasherModuleCDasherComponent

-はい、CDasherComponentにはcppにデストラクタボディがあります。私がこれを投稿したとき、それは.hで宣言されていると思いました。-正式に言及。-これは、ドキュメントを削除するときに誤って追加したブラケットです。-私が理解する限りでは、そうです。私は書いていないautomakeファイルを変更していますが、同じクラスからの同じ継承パターンを持つ他のクラスで機能したパターンに従っているので、愚かな間違いをしない限り(完全に可能です) 、それだけではないと思います。
RyanG 2010年

@RyanG:すべての仮想関数定義をクラス定義に移動してみてください。それらがすべて揃っていることを確認し、結果が変化するかどうかを確認します。
スティーブン

5

おそらく、仮想デストラクタが欠落していることが要因の1つでしょうか?

virtual ~CDasherModule(){};

5

これが私にとって最初の検索結果だったので、チェックする別のことを追加したいと思いました。仮想関数の定義が実際にクラス上にあることを確認してください。私の場合、私はこれを持っていました:

ヘッダーファイル:

class A {
 public:
  virtual void foo() = 0;
};

class B : public A {
 public:
  void foo() override;
};

そして私の.ccファイルで:

void foo() {
  ...
}

これは読むべきです

void B::foo() {
}


3

ここには非常に多くの答えがありますが、私の問題が何であるかをカバーしていないようです。私は次のことをしました:


class I {
    virtual void Foo()=0;
};

そして、別のファイル(もちろん、コンパイルとリンクに含まれています)

class C : public I{
    void Foo() {
        //bar
    }
};

まあこれはうまくいきませんでした、そして私は誰もが話しているエラーを受け取りました。それを解決するには、Fooの実際の定義をクラス宣言の外に移動する必要がありました。

class C : public I{
    void Foo();
};

C::Foo(){
   //bar
}

私はC ++の第一人者ではないので、これがより正しい理由を説明することはできませんが、問題は解決しました。


私はC ++の第一人者ではありませんが、追加の定義ファイルを使用して、同じファイルで宣言と定義を混在させることに関連しているようです。
Terry G Lorber 2017年

1
関数定義がクラス定義内にある場合、それは暗黙的に「インライン」で宣言されました。その時点で、すべての仮想関数がインライン化されました。関数をクラス定義の外に移動すると、その関数は「インライン」関数ではなくなりました。インライン化されていない仮想関数があるため、コンパイラーはvtableをどこに出力するかを知っていました。(より完全な説明はコメントに収まりません。)
JaMiT

3

だから私はWindows XPとMinGWコンパイラでQtを使用していて、これが私を狂わせていました。

基本的にmoc_xxx.cppは、私が追加されても空で生成されました

Q_OBJECT

関数を仮想的、明示的、およびあなたが推測するものは何も機能しないものをすべて削除しても機能しません。最終的に私は行ごとに削除を開始し、私が持っていたことが判明しました

#ifdef something

ファイルの周り。#ifdefがtrueの場合でも、mocファイルは生成されませんでした。

したがって、すべての#ifdefsを削除すると、問題が修正されました。

この問題は、WindowsおよびVS 2013では発生していませんでした。


Q_OBJECT行をコメント化すると、単純なテストアプリがプレーンでビルドされg++ *.cpp ...ます。(素早く簡単なものが必要ですが、qmakeは悲しみに満ちていました。)
Nathan Kidd

2

他のすべてが失敗した場合は、重複を探します。別の投稿でリファレンスを読むまで、コンストラクターとデストラクタへの明示的な最初のリファレンスに誤解されました。それ未解決の方法です。私の場合、パラメーターとしてchar * xmlを使用する宣言を、不必要に面倒なconst char * xmlを使用する宣言に置き換えたと思いましたが、代わりに、新しい宣言を作成し、もう1つはそのままにしました。


2

このエラーを引き起こす可能性はたくさんありますが、それらの多くがエラーを引き起こしていると思います。私の場合、ソースファイルが重複しているため、同じクラスの別の定義がありました。このファイルはコンパイルされましたが、リンクされていなかったため、リンカーはファイルを見つけることができないと不満を言っていました。

要約すると、クラスを十分にじっと見つめていて、考えられる構文の問題が原因である可能性があることがわからない場合は、不足しているファイルや重複したファイルなどのビルドの問題を探します。


2

私の場合、私はQtを使用していてQObjectfoo.cpp(ではない.h)ファイルでサブクラスを定義していました。修正は#include "foo.moc"の最後に追加することでしたfoo.cpp


2

私はそれはまた、あなたがのオブジェクトにリンクしようとすると、またメッセージが表示されますことを言及する価値だと思う任意のクラスがある少なくとも一つの仮想メソッドとリンカが見つけることができないファイルを。例えば:

Foo.hpp:

class Foo
{
public:
    virtual void StartFooing();
};

Foo.cpp:

#include "Foo.hpp"

void Foo::StartFooing(){ //fooing }

コンパイル:

g++ Foo.cpp -c

そしてmain.cpp:

#include "Foo.hpp"

int main()
{
    Foo foo;
}

コンパイルおよびリンク:

g++ main.cpp -o main

私たちのお気に入りのエラーを与えます:

/tmp/cclKnW0g.o:Fooの関数main': main.cpp:(.text+0x1a): undefined reference tovtableのcollect2:エラー:ldが1の終了ステータスを返しました

これは私の理解できない理由から起こります:

  1. Vtableはコンパイル時にクラスごとに作成されます

  2. リンカーはFoo.oにあるvtableにアクセスできません


1

次のシナリオでこのエラーが発生しました

ヘッダーファイル自体でクラスのメンバー関数の実装を定義した場合を考えます。このヘッダーファイルは、エクスポートされたヘッダーです(つまり、コードベースのcommon / includeに直接コピーされる場合があります)。これで、メンバー関数の実装を.cppファイルに分離することにしました。実装を.cppに分離または移動した後、ヘッダーファイルにはクラス内のメンバー関数のプロトタイプのみが含まれるようになります。上記の変更後、コードベースをビルドすると、「undefined reference to 'vtable ...」エラーが発生する場合があります。

これを修正するには、ビルドする前に、common / includeディレクトリにある(変更を加えた)ヘッダーファイルを必ず削除してください。また、作成したばかりの新しい.cppファイルから構築された新しい.oファイルを収容/追加するようにmakefileを必ず変更してください。これらの手順を実行すると、コンパイラ/リンカーは文句を言わなくなります。


奇妙な。ヘッダーを別の場所にコピーする場合、ビルドシステムは、元のファイルが変更されるとすぐに、別のファイルに含める前に、コピーを自動的に更新する必要があります。手動で行う必要がある場合、ねじ込みます。
Offirmo 2013年

1

オブジェクトをアーカイブに追加できないようにするmakeバグが発生したときに、オブジェクトにリンクしようとしたときにこのタイプのエラーが発生しました。

たとえば、intにbioseq.oがあるはずのlibXYZ.aがあるとしましょう。

エラーが発生しました:

combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'

これは、上記のすべてとは異なります。アーカイブの問題で、この欠落しているオブジェクトを呼び出します。


0

次のようなメッセージが表示されることもあります

SomeClassToTest.host.o: In function `class1::class1(std::string const&)':
class1.hpp:114: undefined reference to `vtable for class1'
SomeClassToTest.host.o: In function `class1::~class1()':
class1.hpp:119: undefined reference to `vtable for class1'
collect2: error: ld returned 1 exit status
[link] FAILED: 'g++' '-o' 'stage/tests/SomeClassToTest' 'object/tests/SomeClassToTest.host.o' 'object/tests/FakeClass1.SomeClassToTest.host.o'

別のクラスSomeClassの単体テストをリンクしようとしているときに、クラスFakeClass1の仮想関数を定義し忘れた場合。

//class declaration in class1.h
class class1
{
    public:
    class1()
    {
    }
    virtual ~class1()
    {
    }
    virtual void ForgottenFunc();
};

そして

//class definition in FakeClass1.h
//...
//void ForgottenFunc() {} is missing here

この場合、もう一度class1の偽物をチェックすることをお勧めします。おそらくForgottenFunc、偽のクラスで仮想関数を定義するのを忘れているかもしれません。


0

既存のソース/ヘッダーのペアに2番目のクラスを追加すると、このエラーが発生しました。同じ.hファイル内の2つのクラスヘッダー、および同じ.cppファイル内の2つのクラスの関数定義。

私はこれまでうまく連携して動作することを目的としたクラスを使用してこれを成功させてきましたが、今回はどうやら私には気に入らなかったようです。まだわかりませんが、コンパイルユニットごとに1つのクラスに分割することで修正されました。


失敗した試み:

_gui_icondata.h:

#ifndef ICONDATA_H
#define ICONDATA_H

class Data;
class QPixmap;

class IconData
{
public:
    explicit IconData();
    virtual ~IconData();

    virtual void setData(Data* newData);
    Data* getData() const;
    virtual const QPixmap* getPixmap() const = 0;

    void toggleSelected();
    void toggleMirror();
    virtual void updateSelection() = 0;
    virtual void updatePixmap(const QPixmap* pixmap) = 0;

protected:
    Data* myData;
};

//--------------------------------------------------------------------------------------------------

#include "_gui_icon.h"

class IconWithData : public Icon, public IconData
{
    Q_OBJECT
public:
    explicit IconWithData(QWidget* parent);
    virtual ~IconWithData();

    virtual const QPixmap* getPixmap() const;
    virtual void updateSelection();
    virtual void updatePixmap(const QPixmap* pixmap);

signals:

public slots:
};

#endif // ICONDATA_H

_gui_icondata.cpp:

#include "_gui_icondata.h"

#include "data.h"

IconData::IconData()
{
    myData = 0;
}

IconData::~IconData()
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    //don't need to clean up any more; this entire object is going away anyway
}

void IconData::setData(Data* newData)
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    myData = newData;
    if(myData)
    {
        myData->addIcon(this, false);
    }
    updateSelection();
}

Data* IconData::getData() const
{
    return myData;
}

void IconData::toggleSelected()
{
    if(!myData)
    {
        return;
    }

    myData->setSelected(!myData->getSelected());
    updateSelection();
}

void IconData::toggleMirror()
{
    if(!myData)
    {
        return;
    }

    myData->setMirrored(!myData->getMirrored());
    updateSelection();
}

//--------------------------------------------------------------------------------------------------

IconWithData::IconWithData(QWidget* parent) :
    Icon(parent), IconData()
{
}

IconWithData::~IconWithData()
{
}

const QPixmap* IconWithData::getPixmap() const
{
    return Icon::pixmap();
}

void IconWithData::updateSelection()
{
}

void IconWithData::updatePixmap(const QPixmap* pixmap)
{
    Icon::setPixmap(pixmap, true, true);
}

ここでも、新しいソースとヘッダーのペアを追加し、IconWithDataクラスをそのまま切り取って「そのまま」機能するように貼り付けます。


0

私の場合は、私が余分を持っていた、愚かなものだった"後に#include誤って、何を思いますか?

undefined reference to vtable!

私は何時間も仮想関数にコメントして何も変更されていないか確認するために頭と顔の掌を掻き続けてきました。最後に余分なを削除することで"、すべてが修正されました!これらの種類のものは、リンクエラーではなくコンパイルエラーを引き起こす必要があります。

余計に"、私は意味します:

#include "SomeHeader.h""

0

私の場合、Personという名前の基本クラスと、StudentとProfessorという名前の2つの派生クラスがありました。

プログラムの修正方法は次のとおりです。1。基本クラスですべての関数を作成ましたPure Virtual. 。2。すべての仮想デストラクタを次のように使用しましたdefault ones.


-3

コンストラクター引数の名前がヘッダーファイルと実装ファイルで異なるため、このエラーが発生しました。コンストラクタの署名は

PointSet (const PointSet & pset, Parent * parent = 0);

そして私が実装で書いたことは

PointSet (const PointSet & pest, Parent * parent)

したがって、誤って「pset」を「pest」に置き換えました。コンパイラーは、エラーがまったくないこの1つおよび2つの他のコンストラクターについて文句を言っていました。Ubuntuでg ++バージョン4.9.1を使用しています。また、この派生クラスで仮想デストラクタを定義しても違いはありません(基本クラスで定義されています)。コンストラクターの本体をヘッダーファイルに貼り付けず、クラス内で定義しないと、このバグを発見できなかったでしょう。


7
それは何の違いもありません、あなたは他の場所でエラーを起こし、うっかりそれを修正したに違いありません。
MM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.