続々・NVIパターン

Home > 2006-12 / 実装技術 > This Entry [com : 0][Tb : 0]

2006-12-17

NVIパターンに関する話題は、今回でいったん終わりにします。今回は、NVIパターンそのものといえるのかどうか分かりませんが、どちらかといえばその応用的な話題です。

クラスが何らかのリソース群を管理するような場合、特定のリソースを指定するのに、外部からはID番号や名前(文字列)で指定して、それを内部的にリソースを管理する構造体等へのポインタに変換するといった設計がよくあると思います。

class A
{
public:
  virtual void func(int id)
  {
    resource* p_res = get_resource(id);
    // p_resを使った何らかの処理
  }
  // etc.
private:
  struct resource { ... };
  resource* get_resouce(int id);
  resource res_[256];
};

このような場合、funcが仮想関数だと、上書き(overriding)するたびに、ID番号からresourceへのポインタを変換する必要があり、(この程度であれば大したことはありませんが)それなりに面倒です。

そこで、

class A
{
public:
  void func(int id) { do_func(get_resource(id)); }
  // etc.
private:
  struct resource { ... };
  resource* get_resouce(int id);
  virtual void do_func(resoource* p_res) { ... }
  resource res_[256];
};

のようにすることで、派生クラスで仮想関数を上書きするのがかなり楽になります。このように、公開された非仮想メンバ関数では利用者にとって便利なように、非公開または限定公開の仮想関数は実装者にとって便利なように引数や返却値の持ち方を決定することができます。

次に、多重定義に関してです。やろうとしていることは同じだけれども、引数の与え方を少し変えたいためにメンバ関数を多重定義することはよくあります。例えば、次のように感じです。

class window
{
public:
  void move_resize(int left, int top, int right, int bottom);
  void move_resize(const point& left_top, const point& right_bottom);
  void move_rezize(const point& left_top, int width, int height);
  void move_resize(const rect& region);
  // etc.
};

このように、GUIにおけるウィンドウの移動とサイズ変更を同時に行う関数では、座標や幅・高さの指定方法を複数用意したいことでしょう。しかし、これらをすべて仮想関数にしてしまうと、派生クラスで上書きするのが大変です。

また、どれかひとつだけを仮想関数にして、他の関数からその仮想関数を呼び出す方法もありますが、どれが仮想関数なのか一見して分かりにくく、混乱の元になります。

こうした場合にも、限定公開または非公開の仮想関数を、公開された非仮想メンバ関数から呼び出すことで、セマンティクスが同じ処理を一カ所に集め、しかも見た目にも非常に分かりやすい構造にすることができます。

このように、仮想関数を非公開または限定公開にすることは、いろいろとメリットが大きいと思います。特別な事情がない限り、仮想関数は公開しない方がよさそうです。

Comment

Post a Comment









管理者にだけ表示を許可

Trackback

http://cppemb.blog17.fc2.com/tb.php/101-2d28a8be

C++と組み込み環境 | Page Top▲

New >>
文字列リテラルによる初期化
<< old
続・NVIパターン
ブログ内検索
RSSフィード