9
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

C++イディオム第2回

この記事について

評判が良かったので第2回。気合が続いたら3回めがあるかも。

Erase-Remove

STLのコンテナで特定の要素を削除するコードを書いて下さい。

erase.cpp
std::vector<int> a;
/* aに要素を加えるコードを省略 */
std::remove(a.begin(), a.end(), 100);

これで、消えたかというとそうではありません。
実はremove関数は特定の要素をコンテナの後ろに追いやったあと、先頭の特定の要素へのイテレータを返しているだけだからです。
ほんとに消すにはこのイディオムErase-Removeを使います。

erase-remove.cpp
std::vector<int> a;
/* aに要素を加えるコードを省略 */
a.erase(std::remove(a.begin(), a.end(), 100), v.end());

Execute-Around Pointer

スマートポインターはオブジェクトのライフタイムが尽きた時に自動的に確保されたリソースを削除します。コンストラクタとデストラクタ、演算子のオーバライドをうまく使った書き方で実装されています。
この書き方を応用すると、クラスのメンバ関数が実行された時に特定の処理を実行するスマートポインタが実装できます。

execute-around.cpp
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#で提供されているそれ以上の継承を防ぐクラスをエミュレートするイディオムです。

FinalClass.cpp

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で紐付けられたクラスの全てのプライベートメンバにアクセスすることができるようになります。
しかし、この全てというのが問題です。細かいアクセス制限を設けることができません。
そこで、代理人を挟んでアクセス制限を設けます。

attorney-clien.cpp
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

知ってる。みんな知ってる。ヘッダファイルの二重読み込みを防ぐためのイディオム。
ごめんなさい。知ってるよね。

include_guard.cpp
#ifndef HEADER_NAME
#define HEADER_NAME
/* 実装 */

#endif

全てというわけではありませんが今日ではほとんどのコンパイラが二重読み込みを防止専用のマクロ命令があります。

pragma.cpp
#pragma once

後者のほうが書く量が少ないです。違いはそれだけ。後者を使いましょう。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
9
Help us understand the problem. What are the problem?