70

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

#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しても問題は解決しませんでした。
4

9 に答える 9

154

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

qmakeを実行するだけで、コードに他の問題がないと仮定して実行できます。

于 2010-09-06T10:32:11.690 に答える
51

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

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

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

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

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

virtual const QMetaObject *metaObject() const;

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

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

※TU:翻訳単位。C および C++ の専門用語で、単一のソース ファイルと、そこから推移的に含まれるすべてのヘッダー ファイルを指します。基本的に、コンパイラが単一のソースファイルで動作するときに見るもの。

** ODR: 1 つの定義規則。物事 (関数、クラスなど) が異なる翻訳単位で複数回定義された場合に何が起こるかを定義する、C++ 標準の一連の規則。

*** ABI: アプリケーション バイナリ インターフェイス。オブジェクト ファイルをリンクするために必要な、コンパイル時のコードの編成方法の説明。Common C++ ABI は、相互運用できるように Linux 用のコンパイラが一般的に従う仕様です。

于 2014-11-14T11:13:33.193 に答える
17

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

1 時間程度の試行錯誤の後、最終的にそのクラスを main.cpp からスタンドアロンの hpp ファイルに移動し、.pro (プロジェクト) ファイルを更新すると、プロジェクトは完全に正常にビルドされました。それはここでは問題ではなかったかもしれませんが、とにかく有益な情報になると思いました.

于 2010-07-13T03:18:12.290 に答える
11

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

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

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

于 2010-03-31T19:39:50.367 に答える
6

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

于 2010-11-11T21:09:24.577 に答える
3

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

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

于 2010-03-31T20:57:41.790 に答える
2

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

于 2010-10-08T14:17:49.343 に答える
1

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

次に、.cpp および .h ファイルを追加しました。ついに動作します。

于 2017-01-04T12:22:07.393 に答える