C ++がグラフィカルユーザーインターフェイスの作成に適さない理由

私はC ++が大好きですが...





私はC ++が私のお気に入りの言語であるとすぐに予約します。私はそれを「幼少期から」実際に書いています。 さらに、別のホリバーを開始したり、「ポインター」で測定したりする理由はありません。 この記事は、この言語の悪い経験の説明であり、その側面のいくつかを説明しています。その知識は将来他のプログラマーを助けるでしょう。



ある日、開発中のクラスGUIライブラリに出会いました。 C ++、またはそのクラス、インスタンス、および階層の観点から見ると、この言語は、特にウィジェット、クラスウィンドウ、サブウィンドウなどの要素のGUIコントロールの概念に非常に近いようです。 それにもかかわらず、OO C ++モデルとウィンドウシステムは異なります。 C ++は、トークンカバレッジ、型および階層の静的チェックがコンパイル時に定義された「静的」言語として考えられていました。 一方、Windowsとそのオブジェクトは本質的に動的であり、通常、作成された別のプロシージャまたはブロックのフレームワークの外側に存在します。 ウィジェットの階層は、主に場所、可視性、イベントフローによって決定されます。 ウィンドウおよびコントロールの動的および幾何学的階層、イベントのフローなどのグラフィカルユーザーインターフェイスの基本は、C ++構文またはそのセマンティクスによって直接サポートされていません。 したがって、これらの関数はC ++ GUIコードで再現する必要があります。 これにより、グラフィカルツールまたはウィンドウマネージャーの機能が重複し、コードが「肥大化」し、C ++の「強力な」機能の多くを放棄せざるを得なくなります(たとえば、コンパイル時の型チェック)。 この記事では、C ++ / GUIの「ドッキングなし」の簡単な例を示します。





コンストラクタを作成しないでください(または少なくともコンストラクタを使用しないでください)





結果のクラスが親クラスの仮想メソッドをオーバーライドする場合、基本クラスのコンストラクターが実行されている間はオーバーライドが有効にならないことに留意する必要があります。 これは、オブジェクトがGUIイベントに応答するウィジェットを要求するときに特に面倒です。 Basic_Windowクラスが、画面上にバニラの白黒ウィンドウを作成するように設計されているとします。



class Basic_Window {

public:

Basic_Window(Rect rect) { gt_create_window(rect,visible,this); }

virtual void handle_create_event() { set_background(WHITE); }

};








ここで、 gt_create_window()は、メインのグラフィカルツールキットの低レベルの呼び出しを行います(たとえば、 xvt_win_create() )。 この関数は、ツールキットデータ用のスペースを割り当て、ウィンドウマネージャーに通知し、このオブジェクトをイベント受信者として登録し、上記の例では、画面上のウィンドウへのグラフィカル出力を初期化します。

Basic_Windowのインスタンスを作成したいが、背景が赤だとします。 通常、クラスの動作を変更するには、クラスから抽出し、対応する仮想メソッドをオーバーライドする必要があります。 私たちは書きます:



class RedWindow : public Basic_Window {

virtual void handle_create_event() { set_background(RED); }

public:

RedWindow(Rect rect) : Basic_Window(Rect rect) {}

};

RedWindow red_window(default_rect);








ただし、 red_windowは赤ではなく白に見えます! RedWindowを作成するには、 最初に親を作成する必要があります。 Basic_Window :: Basic_Window()の完了後、 RedWindow仮想テーブルが有効になり、 handle_create_event()メソッドが適用できなくなり、 RedWindow()コンストラクターが実行されます。 Basic_Window()コンストラクターは、オブジェクトへのイベント(CREATEイベントなど)の送信を即座に開始するグラフィカルインスツルメンテーションオブジェクトを登録します。 Basic_Window()コンストラクターはまだ終了していないため(これは保証されていません)、オーバーライドされた仮想メソッドは配置されていません。 したがって、CREATEイベントはBasic_Window :: handle_create_event()によって処理されます。 RedWindowクラスの仮想テーブルは、基本クラスが完全に構築されたとき、つまりウィンドウが既に画面上にあるときにのみ作成されます。 この段階でウィンドウの色を変更すると、迷惑なエラーが発生します。



簡単な回避策があります。各デザイナーがグラフィックツールキットオブジェクトを登録できないようにするためです。 イベント処理は、初期化の終了を派生クラスに制限するような方法で構築されます。 画面上のウィジェットを、メモリ内のアプリケーションのGUIオブジェクトの「顔」と見なすのは非常に魅力的です。 上記の例が示すように、画面とC ++オブジェクト間のこの接続は、実装するのがそれほど簡単ではありません:それらは別々に生まれます。



イベント構文なし





クラスライブラリに、写真をウィンドウに表示するpictwindowクラスGUIが含まれているとします。



class PictWindow {

Picture picture;

public:

virtual void repaint() { gt_draw_pict(picture); }

...

};








画像の特定の領域に小さな長方形をオーバーレイします。 このために、 PictWindowのサブクラスを作成してみます。



class OvWindow : public PictWindow {

Rect rect;

virtual void repaint() { gt_draw_rect(rect); }

};








残念ながら、 OvWindowのインスタンスを作成すると、空のウィンドウには長方形のみが表示され、画像は表示されません。 OvWindow :: repaint()PictWindow :: repaint()をオーバーライドする瞬間から、ウィンドウが描画されるときに最後の関数は呼び出されません。 次のようにOvWindowを実装する必要がありました。



class OvWindow : public PictWindow {

Rect rect;

virtual void repaint() { PictWindow::repaint(); gt_draw_rect(rect); }

public:

OvWindow(void) : PictWindow() {}

};









OvWindowコンストラクター 、コンストラクターと同様にOvWindow :: repaint()メソッドをスーパークラスに遅延させる必要があることを強調するために概説されています。 実際、派生オブジェクトのコンストラクターは、対応するオブジェクトのコンストラクターを最初から呼び出します。 repaint()は、その親:オーバーライドする基本クラスのメソッドを遅延させる必要があります。



結論:貧弱なC ++ / GUI互換性





C ++は、「静的」言語として開発されました。





GUIオブジェクト:





C ++は、メッセージングと送信の動的保護をサポートすることを目的としていません(例外を除く)。 これはすべて、グラフィカルツールとウィンドウマネージャー機能の重複、コードの増加、安全でない関数の使用、C ++の多くの長所の拒否につながります。



結論





もちろん、これらすべての「ひっかかり」は致命的ではありません。 C ++は普遍的で強力な言語であるため、可能なすべての計算アルゴリズムを表現できます。 したがって、アプリケーションがTcl / TkScheme / TkPostscriptなどの動的関数を必要とする場合、 C ++を使用すると、例に従っていつでも何かを行うことができます。 一方、これらすべての特性が存在する言語を使用してみませんか?



All Articles