Qt をはじめよう! 第11回: QObject の派生クラスを作成しよう

前回はシグナルとスロットと、その使い方について解説しました。"次回は独自のシグナルとスロットを作成してみましょう。"と書きましたが、その前に、シグナルとスロットの作成のために必要な QObject の派生クラスの作成について解説することにします。

QObject の派生クラスの作成

前回まで使用してきたサンプルに QObject の派生クラス Object を追加しましょう。

前回までに作成したプロジェクトを開いた状態で ファイル(F) -> ファイル/プロジェクトの新規作成(N)… を選択してください。

Qt Creator 2.0 では新規作成ダイアログがより使いやすくなりました。

「ファイルとクラス」の中から「C++」を選択し、右上のリストの「C++ クラス」を選択し、「選択(C)...」ボタンをクリックします。

C++ クラスウィザード

クラス名: Object

基底クラス:QObject

型情報:QObject を継承

と設定し、「次へ(N)>」をクリックします。

object.h と object.cpp が作成され、プロジェクト example.pro に追加されます。

「完了(F)」 をクリックし、ウィザードを終了してください。

example.pro

Qt のアプリケーション開発ではそのプロジェクトの情報を <プロジェクト名>.pro というプロジェクトファイルで管理します。プロジェクトファイルにはそのプロジェクトに含まれるソースコードとヘッダファイルのリストや、その他のファイルの情報、様々な設定などが含まれます。Qt Creator でプロジェクトを作成した際にはプロジェクトファイルが自動で生成され、ファイルを追加したり削除したりした際にもプロジェクトファイルは更新されます。Qt Creator を使用しない場合には手動で管理していただく必要があります。

Qt Creator でプロジェクトツリーの中から example.pro を開いて、内容を確認してみましょう。

QT       += core gui    # [1]
TARGET = example # [2]
TEMPLATE = app # [3]
SOURCES += main.cpp 
example.cpp
object.cpp # [4]
HEADERS += example.h
object.h # [5]

[1] 使用する Qt のモジュールを指定します

[2] 生成する実行バイナリの名前を指定します

[3] Qt のプロジェクトのテンプレートを設定します

[4] ソースコードの一覧

[5] ヘッダファイルの一覧

Qt ではこの(プラットフォームに依存しない形式の)プロジェクトファイルを、qmake という名前のツールを使用して、各プラットフォームのビルド環境に応じた形式(Makefile等)に変換しビルドをできるようにします。Qt Creator では Qt のプロジェクトを新規作成した際やプロジェクトファイルを変更した後の、ビルドの一番最初に qmake が実行されています。

ここでは qmake の使い方やプロジェクトファイルの形式に付いての詳細な解説は省略します。詳細は [qt "qmake Manual" l=qmake-manual] をご覧ください。

object.h

Qt Creator で作成した Object クラスのヘッダファイルです。

#ifndef OBJECT_H
#define OBJECT_H

#include <QObject> // [1]

class Object : public QObject // [2]
{
Q_OBJECT // [3]
public:
explicit Object(QObject *parent = 0); // [4]

signals: // [5]

public slots: // [6]

};

#endif // OBJECT_H

[1] QObject クラスのヘッダファイルをインクルードします

[2] QObject クラスのサブクラス Object クラスを定義します

[3] [qt "Q_OBJECT" l=qobject m=#Q_OBJECT] はマクロで、QObject (とその派生クラス)を継承したクラスを作成する際に、定義の一番初めに記述します。第7回 Qt のオブジェクトモデルを理解しよう で解説した Qt のオブジェクトモデルの様々な機能の実現に必要なコードに置き換えられます。QObject の派生クラスを作成する際には必ず Q_OBJECT マクロを記述するよう心がけてください。

[4] デフォルトのコンストラクタの定義です。Qt のオブジェクトではこのように親オブジェクト(ウィジェットの場合は親ウィジェット)を指定できるようにしておくのが一般的です。

[5] シグナル宣言用のスコープです。シグナルはこのスコープに追加することになります。このスコープは、後述の moc がシグナルの情報を取得するためのキーワードとなります。この "signals" は実際にはマクロとして定義されていて、コンパイル時に "protected" に置換されます。独自のシグナルを持たない場合にはこのスコープの宣言は必要ありません。

[6] public なスロット宣言用のスコープです。スロットの場合はアクセスレベルに応じて public slots:、protected slots:、private slots: というスコープが使い分けられます。"slots" も "signals" 同様、moc に対するキーワードの目的で使用され、コンパイル時にはマクロの展開によって ""(空文字列) に置換されます。独自のスロットを持たない場合には各スロット用のスコープの宣言は必要ありません。

object.cpp

Object クラスの実装ファイルです。

#include "object.h"

Object::Object(QObject *parent) :
QObject(parent)
{

}

ヘッダファイルをインクルードし、Object のコンストラクタで基底クラスのコンストラクタを呼び出しています。

以上が、QObject の派生クラスを作成する際の典型的なヘッダファイル/ソースファイルになります。

ビルド

それでは Qt Creator でビルドをしてください。Alt+4(Mac では Command+4) で表示されるコンパイル出力を眺めてみると、g++ による cpp ファイルのコンパイルに混じって、以下のように moc コマンドが実行されている事を確認してみてください。

/home/tasuku/qtsdk-2010.04/qt/bin/moc -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/home/tasuku/qtsdk-2010.04/qt/mkspecs/linux-g++ -I../example -I/home/tasuku/qtsdk-2010.04/qt/include/QtCore -I/home/tasuku/qtsdk-2010.04/qt/include/QtGui -I/home/tasuku/qtsdk-2010.04/qt/include -I. -I../example -I. ../example/object.h -o moc_object.cpp

今回はクラスを追加しただけで、それを使用するコードは書いていませんので、実行結果は前回と変わりません。

メタオブジェクトコンパイラ(moc)

上記で実行されている moc はメタオブジェクトコンパイラ(Meta-Object Compiler)と呼ばれ、ヘッダファイルやソースファイルを解析し、Qt のオブジェクトシステムに必要なメタオブジェクト情報を生成する為の Qt のツールです。QObject の派生クラスのクラス情報や、追加したシグナルやスロット、プロパティなどの情報が moc が自動生成するファイル moc_<ソースファイル名(.cpp)> に含まれます。qmake が自動でヘッダファイルを捜索し、Q_OBJECT マクロが見つかった場合にはそのファイルに対して moc を実行するルールを作成します。生成されたソースファイルはコンパイルされ、バイナリの一部として含まれます。

ヘッダファイルではなく、ソースファイル(.cpp)内で QObject の派生クラスを定義する場合には、そのままでは qmake による自動的なルールの作成は行われませんので注意が必要です。この場合、そのソースファイルの中で、クラスの定義以降の場所に

#include "&lt;ソースファイル名(拡張子なし)&gt;.moc"

という行を追加する必要があり、これにより qmake はこの moc ファイルを生成するルールを作成するようになります。この行を追加した後には一度手動で qmake を実行する必要があります。Qt Creator では ビルド(B) -> qmake 実行 を実行してください。

例えば、main.cpp ファイル内に QObject の派生クラスを定義する場合には、

#include "main.moc"

という行の追加が必要です。なお、慣例的にこの行はファイルの一番最後に書かれることが多いようです。

この行がない場合にはコンパイル時に以下のようなエラーが出るでしょう。

undefined reference to vtable for &lt;クラス名&gt;

moc についての詳細はドキュメント [qt "Using the Meta-Object Compiler (moc)" l=moc] を参照してください。

おわりに

今回は QObject の派生クラスの作成をするとともに、Qt のプロジェクトファイルと、ビルド時に使用するツール qmake と moc について簡単に解説しました。次回は独自のシグナルとスロットを作成し、使用してみましょう。


Blog Topics:

Comments