この記事について
評判が良かったので第2回。気合が続いたら3回めがあるかも。
Erase-Remove
STLのコンテナで特定の要素を削除するコードを書いて下さい。
std::vector<int> a;
/* aに要素を加えるコードを省略 */
std::remove(a.begin(), a.end(), 100);
これで、消えたかというとそうではありません。
実はremove関数は特定の要素をコンテナの後ろに追いやったあと、先頭の特定の要素へのイテレータを返しているだけだからです。
ほんとに消すにはこのイディオムErase-Removeを使います。
std::vector<int> a;
/* aに要素を加えるコードを省略 */
a.erase(std::remove(a.begin(), a.end(), 100), v.end());
Execute-Around Pointer
スマートポインターはオブジェクトのライフタイムが尽きた時に自動的に確保されたリソースを削除します。コンストラクタとデストラクタ、演算子のオーバライドをうまく使った書き方で実装されています。
この書き方を応用すると、クラスのメンバ関数が実行された時に特定の処理を実行するスマートポインタが実装できます。
class ExecutePointer {
public:
class proxy {
public:
proxy (vector<int> *v) : vect (v) {
std::cout << "Before size is: " << vect->size();
}
vector<int> * operator -> () {
return vect;
}
~proxy () {
std::cout << "After size is: " << vect->size ();
}
private:
vector <int> * vect;
};
ExecutePointer(vector<int> *v) : vect(v) {}
proxy operator -> () {
return proxy (vect);
}
private:
vector <int> * vect;
};
int main()
{
ExecutePointer vector(new std::vector<int>);
vector->push_back (10);
vector->push_back (20);
return 0;
}
Final Class
JavaやC#で提供されているそれ以上の継承を防ぐクラスをエミュレートするイディオムです。
class Finalize
{
~Finalize() {}
friend class sealed;
};
class sealed : virtual Finalize
{
/* 省略 */
};
class Forbidden : public sealed
{
/* 省略 */
//! これはインスタンス化できない
};
friendによりsealedのみデストラクタにアクセスが許可されています。
そのためsealedはインスタンス化できますが、Forbiddenはできません。
残念ながら、エミュレートなのでJavaやC#のファイナルクラスと異なる点があります。
このイディオムの場合Forbiddenがインスタンス化されない限りエラーが発生しません。
さらに、クラスがインスタンス化されなくても存在できる静的メンバ変数・関数は継承することができてしまいます。
Friendship and Attorney-Client
友情と代理人と客。響きはいいけどよく意味のわからない名前ですね。
このイディオムはfriendクラスの欠点を補うためのイディオムです。
friendクラスを使うと他のクラスからfriendで紐付けられたクラスの全てのプライベートメンバにアクセスすることができるようになります。
しかし、この全てというのが問題です。細かいアクセス制限を設けることができません。
そこで、代理人を挟んでアクセス制限を設けます。
class Client
{
private:
int a;
float b;
double c;
friend class Attorney;
};
class Attorney {
public:
static int readA(const Client& client) { // 変数aのreadアクセス許可
return client.a;
}
static void writeB(Client& client, const float& b) { // 変数bのwriteアクセス許可
c.b = b;
}
static int& read_writeC(Client& c) {
return client.c;
}
friend class MyClass;
};
class MyClass {
// Attorneyクラスのプライベートに制限されたアクセス可能
};
Include Guard Macro
知ってる。みんな知ってる。ヘッダファイルの二重読み込みを防ぐためのイディオム。
ごめんなさい。知ってるよね。
#ifndef HEADER_NAME
#define HEADER_NAME
/* 実装 */
#endif
全てというわけではありませんが今日ではほとんどのコンパイラが二重読み込みを防止専用のマクロ命令があります。
#pragma once
後者のほうが書く量が少ないです。違いはそれだけ。後者を使いましょう。