web-dev-qa-db-ja.com

Vtableへの未定義の参照

だから、私は悪名高い恐ろしさを得ています

'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
271
RyanG

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

回答とGCC FAQへのリンクをありがとう。本当の理由でこの問題が起こるのを避けるために、私はそれを読むことを確実にするでしょう。

49
RyanG

GCC FAQ にエントリがあります。

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

346
Alexandre Hamez

それが価値があるもののために、仮想デストラクタでボディを忘れることは以下を生成します:

`vtable for CYourClass 'への未定義の参照。

エラーメッセージは詐欺的なので、私はメモを追加しています。 (これはgccバージョン4.6.3のものでした。)

139
Dan

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

41
gluk47

以下の状況によっても、vtableへの未定義の参照が発生する可能性があります。これを試してみてください。

クラスAの内容:

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

クラスBの内容:

  1. 上記の関数の定義A。
  2. 上記の関数の定義.

Class C Contains:クラスAから派生するクラスCを作成しています。

コンパイルしようとするとエラーとしてクラスCのvtableへの未定義の参照を取得します。

理由:

functionAは純粋仮想として定義され、その定義はクラスBで提供されています。functionBは仮想として定義されているので(NOT PURE VIRTUAL)、クラスA自体で定義を検索しようとしますが、クラスBで定義します。

溶液:

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

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

38
Will

私はちょうどあなたがチェックすることができるこのエラーのための別の原因に遭遇しました。

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

virtual int foo(int x = 0);

そしてサブクラスは

int foo(int x) override;

問題は"=0"が括弧の外側にあるはずだったというタイプミスでした。

virtual int foo(int x) = 0;

それで、あなたがこれよりずっと下にスクロールしている場合、あなたはおそらく答えを見つけることができませんでした - これはチェックするべき他の何かです。

16
Philip Thomas

ここにはさまざまな答えの中で起こっている憶測がたくさんあります。以下に、このエラーを再現するためのごくわずかなコードを示し、それがなぜ発生するのかを説明します。

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

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

IBase.hppの= 0を削除すると、エラーを再現できます。私はこのエラーが出ます:

~/.../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のメソッドは仮想として宣言されていますが、純粋な仮想ORがその定義を提供するものとしてマークするのを忘れていました。

別れのヒント

他のすべてが失敗するならば、このエラーをデバッグする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をライブラリとして指定する必要はありません。

15
Shital Shah

GNU C++コンパイラは、オブジェクトの仮想関数の定義が複数のコンパイル単位にまたがる場合に備えて、vtableをどこに置くかを決定しなければなりません(たとえば、オブジェクト仮想関数定義の一部が他の.cppファイルに他の.cppファイルなど。

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

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

副作用として、この特定の仮想関数についてのみ、関数fooが見つからないのような従来のリンカエラーが発生することはありません。

11
Iulian Popa

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

11
Hazok

さて、これに対する解決策はあなたが定義を見逃しているかもしれないということです。以下の例を参照して、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
Sammy

クロスポストするのではなく。 inheritance を扱っているのであれば、2回目のグーグルヒットは私が逃したものでした。すべての仮想メソッドを定義する必要があります。

といった:

virtual void fooBar() = 0;

詳細はansware vtableと継承へのC++の未定義の参照 を参照してください。それがすでに上で言及されていることに気づいただけで、だれかがそれを助けるかもしれないということを知ってください。

7
  • CDasherComponentがデストラクタの本体を持っていますか?それは絶対にここにはありません - 問題はそれが.ccファイルにあるかどうかです。
  • スタイルの観点から、CDasherModuleはそのデストラクタvirtualを明示的に定義するべきです。
  • CGameModuleの最後(}の後)に余分な}; // for the classがあるようです。
  • CGameModuleCDasherModuleCDasherComponentを定義するライブラリに対してリンクされていますか?
6
Stephen

おそらく、仮想デストラクタを見逃していることが原因です。

virtual ~CDasherModule(){};
5
DevByStarlight

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

ヘッダファイル

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

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

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

void foo() {
  ...
}

これは読むべきです

void B::foo() {
}
5
RedSpikeyThing

たくさんの答えがここにありますが、どれも私の問題が何であるかをカバーしていないようでした。私は以下のものを持っていました:


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

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

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

まあこれはうまくいきませんでした、そして私は皆が話しているエラーを得ました。それを解決するために、私はFooの実際の定義をクラス宣言から外す必要がありました。

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

C::Foo(){
   //bar
}

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

3
Nathan Daniels

それで私はWindows XPとMinGWコンパイラでQtを使っていました、そしてこのことは私を夢中にさせました。

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

Q_OBJECT

機能を仮想的、明示的にしているものすべてを削除してもうまくいかない。最後に1行ずつ削除し始めたところ、次のようになりました。

#ifdef something

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

そのため、#ifdefをすべて削除すると問題が解決しました。

これは、WindowsとVS 2013では起きていませんでした。

3
Daniel Georgiev

多分違います。間違いなく~CDasherModule() {}がありません。

3
Bretzelus

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

2
Stefan Monov

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

2
Jerry Miller

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

要約すると、クラスを十分に見つめていて、それが原因で発生している可能性のある構文の問題を確認できない場合は、ファイルが見つからないかファイルが重複しているなどのビルドの問題を探します。

2
crw4096

オブジェクトがアーカイブに追加されないというバグが発生したときにオブジェクトにリンクしようとしていた状況で、この種のエラーが発生しました。

LibXYZ.aにintのbioseq.oが含まれているはずですが、そうではないとします。

私はエラーを得ました:

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

これは上記のすべてとは異なる終了です。アーカイブの問題では、この不足しているオブジェクトと呼びます。

1
Kemin Zhou

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

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

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

1

既存のソース/ヘッダーのペアに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
AaronD

vtableとは何ですか?

修正を試みる前に、エラーメッセージが何を言っているかを知ることが役立つ場合があります。高いレベルから始めて、さらに詳細に取り組みます。そうすれば、人々はvtableの理解に慣れたら先にスキップできます。 …そして今、先をスキップしている人々がたくさんいます。 :)こだわりのある人向け:

Vtableは、基本的に polymorphismin C++ の最も一般的な実装です。 vtableが使用される場合、すべてのポリモーフィッククラスにはプログラムのどこかにvtableがあります。クラスの(隠された)staticデータメンバーと考えることができます。多態性クラスのすべてのオブジェクトは、その最も派生したクラスのvtableに関連付けられています。この関連付けを確認することにより、プログラムはそのポリモーフィックマジックを実行できます。 重要な注意事項:vtableは実装の詳細です。ほとんどの(すべて?)C++コンパイラはvtableを使用して多態的な動作を実装しますが、C++標準では必須ではありません。私が紹介している詳細は、典型的または合理的なアプローチです。 コンパイラはこれから逸脱することが許可されています!

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

名前「vtable」は「virtual functiontable」に由来します。 (仮想)関数へのポインターを格納するテーブルです。コンパイラーは、表のレイアウト方法についてその規則を選択します。単純なアプローチは、クラス定義内で宣言された順序で仮想関数を実行することです。仮想関数が呼び出されると、プログラムはオブジェクトのvtableへのポインターに従い、目的の関数に関連付けられたエントリに移動し、保存された関数ポインターを使用して正しい関数を呼び出します。この作品を作成するためのさまざまなトリックがありますが、ここではそれらを取り上げません。

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

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

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

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

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

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

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

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

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

(OK、この質問が出されたときは3番で十分だったかもしれません。)

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

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

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


対処方法の詳細はさまざまで、場合によっては別の質問に分岐することもあります( 未定義の参照/未解決の外部シンボルエラーとは何ですか? )。ただし、新しいプログラマを混乱させる可能性のある特定のケースで何をすべきかの例を提供します。

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

virtual ~A() = default;

または

virtual ~A() {}

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

virtual ~A();

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

A::~A() {}

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

0
JaMiT

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

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
Yuriy

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

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 'collect2の関数main': main.cpp:(.text+0x1a): undefined reference tovtableに:エラー:ldが1つの終了状況を返しました

これは私の未解決の理由から起こる:

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

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

0
Adam Stepniak