web-dev-qa-db-ja.com

Qtリンカーエラー:「vtableへの未定義の参照」

これは私のヘッダーです:

#ifndef BARELYSOCKET_H
#define BARELYSOCKET_H

#include <QObject>
//! The First Draw of the BarelySocket!

class BarelySocket: public QObject
{
    Q_OBJECT

public:
    BarelySocket();
public slots:
    void sendMessage(Message aMessage);
signals:
    void reciveMessage(Message aMessage);

private:
    //   QVector<Message> reciveMessages;
};

#endif // BARELYSOCKET_H

これは私のクラスです:

#include <QTGui>
#include <QObject>
#include "type.h"
#include "client.h"
#include "server.h"

#include "barelysocket.h"

BarelySocket::BarelySocket()
{
    //this->reciveMessages.clear();
    qDebug("BarelySocket::BarelySocket()");
}

void BarelySocket::sendMessage(Message aMessage)
{
}

void BarelySocket::reciveMessage(Message aMessage)
{
}

リンカーエラーが表示されます。

undefined reference to 'vtable for BarelySocket'
  • これは、実装されていない仮想メソッドがあることを意味します。しかし、私のクラスには仮想メソッドはありません。
  • 私はそれが原因だと思ってベクトルをコメントアウトしましたが、エラーは消えませんでした。
  • Messageは複雑なstructですが、intを使用しても問題は修正されませんでした。
59
Thomas

Q_OBJECTマクロに新しい呼び出しを追加するたびに、qmakeを再度実行する必要があります。参照しているvtablesの問題は、それに直接関連しています。

Qmakeを実行するだけで、コードに他の問題がないと仮定して準備ができているはずです。

138
Michael

私は問題を解決する多くの方法を見てきましたが、なぜそれが起こるかについての説明はありませんので、ここに行きます。

コンパイラは、仮想関数(直接宣言または継承)を持つクラスを見つけると、そのクラスのvtableを生成する必要があります。クラスは一般にヘッダーで定義されるため(したがって、複数の翻訳単位で表示されるため)、問題はvtableをどこに配置するかです。

一般に、問題は、クラスが定義されているすべてのTUでvtableを生成し、リンカーに重複を排除させることで解決できます。クラス定義はすべての発生で同じである必要があるためODRによってこれは安全ですが、コンパイルが遅くなり、オブジェクトファイルが膨張し、リンカがより多くの作業を行う必要があります。

したがって、最適化として、可能な場合、コンパイラは特定のTUを選択してvtableを配置します。一般的なC++ ABIでは、このTUはクラスのキー関数が実装されているもの。キー関数は、クラスで宣言されているが定義されていない最初の仮想メンバー関数です。

Qtクラスの場合、通常はQ_OBJECTマクロで始まり、このマクロには宣言が含まれます

virtual const QMetaObject *metaObject() const;

これはマクロ内の最初の仮想関数であるため、通常はクラスの最初の仮想関数、したがってそのキー関数になります。したがって、コンパイラはほとんどのTUでvtableを発行せず、metaObjectを実装するTUのみを発行します。そして、この関数の実装は、ヘッダーを処理するときにmocによって自動的に書き込まれます。したがって、ヘッダーをmoc処理して新しい.cppファイルを生成し、その.cppファイルをコンパイルに含める必要があります。

したがって、QObject派生クラスを定義する新しいヘッダーがある場合は、qmakeを再実行して、新しいヘッダーでmocを実行してコンパイルするようにメイクファイルを更新する必要があります。結果の.cppファイル。

41
Sebastian Redl

何かをテストするために作成した小さな「main.cpp」ファイル内に小さなクラスを作成した後、このエラーに遭遇しました。

1時間ほどかけて、最終的にそのクラスをmain.cppからスタンドアロンhppファイルに移動し、.pro(プロジェクト)ファイルを更新して、プロジェクトを完全に正常にビルドしました。ここでは問題ではなかったかもしれませんが、とにかく有用な情報になると考えました。

15
David

経験から:多くの場合、qmake && make clean && makeは役立ちます。私は個人的には、変更の発見/キャッシュ効果/ xxxxxが何であれわからないことを認識しています。理由を言うことはできませんが、この種のエラーが発生したときに最初に行うことです。

ところで。 > recive <にタイプミスがあります

コンストラクター(初期化子リスト内)でQObjectコンストラクターを呼び出すのを忘れました。 (ただし、エラーは解決しません)

11
Ronny Brendel

私にとっては、ビルドログからmocが呼び出されていないことに気付きました。 Clean Allは役に立ちませんでした。そこで.pro.userを削除し、IDEを再起動しました。

4
lstipakov

QOBjectからクラスを派生する(およびQ_OBJECTマクロを使用する)場合、コンストラクタークラスとデストラクタクラスの両方を明確に定義および作成することを忘れないでください。コンパイラのデフォルトのコンストラクタ/デストラクタを使用するだけでは不十分です。 qmakeのクリーニング/実行(およびmoc_ファイルの消去)に関するアドバイスは引き続き適用されます。これは私の同様の問題を修正しました。

2
Pete

シグナルに実装を含めることはできません(これはQtによって生成されます)。 .cppファイルからreciveMessage実装を削除します。これで問題が解決する場合があります。

私が見た他のこと:BarelySocketクラスはQObjectを継承するため、破壊時の問題を回避するために仮想デストラクタが必要です。これは、他のクラスから継承するすべてのクラスに対して実行する必要があります。

2

私はこのエラー時間に苦労しました。 .cppファイルと.hファイルを別のフォルダー(!!)に入れることで解決しました。次に、.proファイルにフォルダーを追加しました:INCLUDEPATH + = $$ {_ PRO_FILE_PWD _} /../ MyClasses/CMyClassWidget

そして、.cppおよび.hファイルを追加しました。やっと動作します。

1
Sunny127

私はあなたがこれを見るかもしれない別の理由を見つけました-qmakeはあなたがこのエラーを得るかもしれない非標準的な方法でそれらを修正した場合あなたのクラスファイルを解析するので。私の場合、QDialogから継承したカスタムダイアログがありましたが、WindowsまたはOSXではなく、Linux用にビルドするときにコンパイルして実行するだけでした。私はただ#ifdef __linux__クラスはコンパイルされませんでしたが、Linuxでは__linux__は、qmakeをスローしていると定義されました。

0
Preston