Qtプログラミングの特徴であるシグナル(signal)とスロット(slot)について気になって調べたことまとめ。
Qtプログラミングの特徴のひとつとしてここでも書いたようにsignalとslotがあります。
まずどんなコードになるかというと
class Test1 : QObject { Q_OBJECT Test1(){ QObject::connect(&button, SIGNAL(clicked()), this, SLOT(update())); QObject::connect(&browser, SIGNAL(textChanged()), this, SIGNAL(updated())); } // 略 public slots: void update() { browser.append("unko!");} signals: void updated(); // 略 private: QButton button; QTextBrowser browser; }
こんな感じ。いろいろ欠けてはいるけどとりあえずウィンドウにボタン(button)とテキスト領域(browser)が配置されていると想像してください。なんとなくコードから予想できると思いますが、ボタンが押されるとテキスト領域に
unko!
が追加されます。
ここで気になるのは、slotsはpublic修飾されていることです。これはどういうことかというと、スロットはQtの概念上ではシグナルをうけるスロットですが、同時に普通のC++の関数でもあるということです。普通の関数なのでアクセス修飾子の影響をうけます。
さて、ではシグナルによってスロットとして呼び出されるときにはアクセス制限はどうなるのか、APIドキュメントのSignals and Slotsにはこう書いてあります。
Since slots are normal member functions, they follow the normal C++ rules when called directly. However, as slots, they can be invoked by any component, regardless of its access level, via a signal-slot connection. This means that a signal emitted from an instance of an arbitrary class can cause a private slot to be invoked in an instance of an unrelated class.
つまり、
スロットは普通の関数として呼んだらいつものルールで呼び出しもとの制限があるけどスロットとしてシグナルから呼べばぶっちゃけ誰でもprivateなメソッド呼べちゃうよ!(超絶意訳)
ということらしいです。このことは気になっていたのでドキュメントを読む前に実際にテストコードを書いていたのですが、ばっちりprivateな関数を呼べていました。
これはちょっと気をつけなければいけないかもしれません。メンバとして持っている別のオブジェクトからのシグナルを受けるためにprivate slots: void hoge();とか作っておくと、privateなので外から直接呼び出しは受けませんが、うっかり別のシグナルと接続できてしまうのでその場合には困ったことになりかねません。シグナルとスロットの機構ではシグナルを発信したのかもわからないのでスロット側で分岐させることもできません。
ただ、シグナルとスロットは他人同士をゆるくつなげるだと思うので、内部メンバと強く結びつけたり、呼び出しもとが誰なのか判断したい場合はイベント機構をつけばほぼ解決すると思います。Qtのイベント機構は階層的なイベントハンドラを利用することができるので非常に柔軟な処理ができます。(得てして柔軟とか謳っている場合それはけっこう面倒なものだったりするのですが、それはしかたないですねー。)