ローカル関数
Problem
ある関数の呼び出しを特定の関数のみに限定した場合、どのようにすれば良いでしょうか?
Solution
以下のような関数is_oddがあったとします。
ある関数の呼び出しを特定の関数のみに限定した場合、どのようにすれば良いでしょうか?
Solution
以下のような関数is_oddがあったとします。
#include <vector>
#include <algorithm>
#include <iostream>
bool is_odd(int x)
{
return x % 2;
}
int main()
{
std::vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
std::cout << *std::find_if(v.begin(), v.end(), is_odd) << std::endl;
return 0;
}
main以外からはis_oddの呼び出しを禁止したい場合、staticメンバ関数を用いたHackで関数内に関数を定義することができます。ローカル変数ならぬローカル関数です。
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
struct local {
static bool is_odd(int x)
{
return x % 2;
}
};
std::vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
std::cout << *std::find_if(v.begin(), v.end(), local::is_odd) << std::endl;
return 0;
}
オブジェクト指向における再利用のためのデザインパターン
![]() | オブジェクト指向における再利用のためのデザインパターン エリック ガンマ、ラルフ ジョンソン 他 (1999/10) ソフトバンククリエイティブ この商品の詳細を見る |
デザインパターンのバイブル、オブジェクト指向における再利用のためのデザインパターンです。
本書がプログラミングにおけるデザインパターンの始まりであり、本書出版以降、デザインパターンは全てのプログラマが必須の知識となりました。
現在では、デザインパターンに関する良書が多く出版されていますが、共通語彙としてのデザインパターンを語る上で、外すことができない重要な原典となっています。また、著者の4人組みは敬意を込めてGoF(Gang of Four)と呼ばれており、本書はGoF本と呼ばれることもあります。
NVIイディオム
Problem
NVIイディオムとは、どのようなイディオムでしょうか?
Solution
例えば、以下のようなコードがあったとします。
この問題を回避するためには、Template Methodパターンを用います。
Template Methodパターンとは、オブジェクト指向における再利用のためのデザインパターンによると
実際に、Template Methodパターンを適用すると次のようなコードになります。
まず、インターフェースとしてのsomething()ですが、something()自身が仮想関数の呼び出しを行っているので、something()自身は仮想関数である必要はありません。むしろ、非仮想仮想である方が望ましいです。また、doSomething()に関しては、メンバ関数であるsomething()から呼び出せれば良いだけなのでpublicである必要はありません。privateもしくはprotectedに変更しましょう。
NVIイディオムとは、どのようなイディオムでしょうか?
Solution
例えば、以下のようなコードがあったとします。
#include <iostream>
#include <tr1/memory>
class Base {
public:
virtual ~Base() {}
virtual void something(const char* text)
{
std::cout << "Base::something(" << text << ")" << std::endl;
}
};
class Derived : public Base {
public:
virtual ~Derived() {}
virtual void something(const char* text)
{
std::cout << "Derived::something(" << text << ")" << std::endl;
}
};
int main()
{
std::tr1::shared_ptr<Base> p(new Derived);
p->something("message");
return 0;
}
ここで assert(text); のような事前条件を追加したくなった場合、2つの仮想関数に対して修正を行わなければなりません。
class Base {
public:
virtual ~Base() {}
virtual void something(const char* text)
{
assert(text);
std::cout << "Base::something(" << text << ")" << std::endl;
}
};
class Derived : public Base {
public:
virtual ~Derived() {}
virtual void something(const char* text)
{
assert(text);
std::cout << "Derived::something(" << text << ")" << std::endl;
}
};
これではコードの重複を避けるというプログラミングの基本原則に反することになります。この問題を回避するためには、Template Methodパターンを用います。
Template Methodパターンとは、オブジェクト指向における再利用のためのデザインパターンによると
1 つのオペレーションにアルゴリズムのスケルトンを定義しておき、その中のいくつかのステップについては、 サブクラスでの定義に任せることにする。Template Method パターンでは、アルゴリズムの構造を変えずに、 アルゴリズム中のあるステップをサブクラスで再定義する。とあります。
実際に、Template Methodパターンを適用すると次のようなコードになります。
class Base {
public:
virtual ~Base() {}
virtual void something(const char* text)
{
assert(text);
something(text);
}
virtual void doSomething(const char* text)
{
std::cout << "Base::something(" << text << ")" << std::endl;
}
};
class Derived : public Base {
public:
virtual ~Derived() {}
virtual void doSomething(const char* text)
{
std::cout << "Derived::something(" << text << ")" << std::endl;
}
};
Template Methodパターンを適用することで、コードの二重化を避けることができましたが、更に考えを進めてみましょう。まず、インターフェースとしてのsomething()ですが、something()自身が仮想関数の呼び出しを行っているので、something()自身は仮想関数である必要はありません。むしろ、非仮想仮想である方が望ましいです。また、doSomething()に関しては、メンバ関数であるsomething()から呼び出せれば良いだけなのでpublicである必要はありません。privateもしくはprotectedに変更しましょう。
class Base {
public:
virtual ~Base() {}
void something(const char* text)
{
assert(text);
something(text);
}
private:
virtual void doSomething(const char* text)
{
std::cout << "Base::something(" << text << ")" << std::endl;
}
};
class Derived : public Base {
public:
virtual ~Derived() {}
private:
virtual void doSomething(const char* text)
{
std::cout << "Derived::something(" << text << ")" << std::endl;
}
};
これがNVI(Non Virtual Interface)と呼ばれるイディムです。

