リソースの解放忘れを防ぐには? Effective C++ 14項を読んで

Scott Mayers著, 小林健一郎訳, Effective C++ 第3版, 丸善出版(2014) を1日1項目読んでいます。

今日は14項を読みました。

リソース(メモリ、ファイルディスクリプタミューテックスロック、DB接続、ネットワークソケットなど)の解放忘れを防ぐにはどうしたらよいでしょうか? RAIIオブジェクトをリソースを渡すことで生成すればよい。そして、RAIIオブジェクトとしては、C++11のstd::shared_ptrを使うとよい、ということがわかりました。

どうして、リソースの解放忘れが起こるのでしょうか? それは、例えば、リソースとしてメモリを考えると、ある関数内でヒープ領域にメモリを確保すると、その関数を抜ける前に、メモリを解放する必要があります。

void f(){
    A* a = new A();
   ... // ここで、returnしたり、例外が発生したりするとdeleteが実行されない。
   delete a;
}

しかし、上のように、deleteする前にreturnしたり、例外が発生したりするとdeleteが実行されず、メモリリークになります。何らかの理由で関数を抜ける際に、必ずdeleteが実行されるような仕組みはないでしょうか? これを解決するアイディアはデストラクタです。

デストラクタは、そのオブジェクトの寿命が終わると実行されます。つまり、関数内で、あるオブジェクトをポインタなどのリソースを渡すことで生成すれば、関数を抜けるときに、オブジェクトのデストラクタが実行されるので、このデストラクタでポインタをdeleteしてもらうことができます。

このデストラクタをもつオブジェクトのことをRAIIオブジェクトといいます。既に用意されているRAIIオブジェクトとして、C++11のstd::shared_ptrがあります。また、std::shared_ptrをstaticでないメンバとしてもつオブジェクトもRAIIオブジェクトになります。なぜなら、デストラクタが呼ばれるとき、メンバのデストラクタも呼ばれるからです。

リソースがメモリのときには、リソースへのポインタをstd::shared_ptrのコンストラクタに渡します。すると、std::shared_ptrのデストラクタでこのポインタがdeleteされます。(delete[]でないことに注意)

リソースがメモリでないときには、リソースへのポインタをdeleteするとともに、何らかの解放処理(デリータ)が必要になります。リソースへのポインタを引数とする関数オブジェクト、すなわちデリータをstd::shared_ptrのコンストラクタに渡せます。

リソースがミューテックスロックの場合のサンプル

#include <memory>
#include <iostream>

class Mutex {
    public:
    void lock(){
        std::cout << "locked\n";
    }

    void unlock(){
        std::cout << "unlocked\n";
    }
};

void lock(Mutex* pm){
    pm->lock();
}

void unlock(Mutex* pm){
    pm->unlock();
}

class Lock {
    public:
    Lock(Mutex* pm)
    : mutexPtr(pm, unlock)
    {
        lock(mutexPtr.get());
    }

    ~Lock(){}

    private:
    std::shared_ptr<Mutex> mutexPtr;
};

void test(const Lock& ml){
    std::cout << "test start\n";
    {
        Lock ml2(ml);
        std::cout << "in critical session in test...\n";
    }
    std::cout << "test end\n";
}

int main(int argc, char* argv[]){
    Mutex m;
    std::cout << "start\n";
    {
        Lock ml1(&m);
        std::cout << "in critical session...\n";
        test(ml1);
    }
    std::cout << "end\n";

    return 0;
}

コンパイル

$ clang++ -std=c++11 Lock.cpp -o lock

実行

$ ./lock
start
locked
in critical session...
test start
in critical session in test...
test end
unlocked
end

Scott Mayersさんのブログhttp://scottmeyers.blogspot.jp/も読んでみようと思います。

国立近代美術館 熊谷守一展覧会

奥さんと一緒に国立近代美術館の熊谷守一没後40年の回顧展に行ってきた。
東京駅の丸善が入っているオアゾがある出口から出て、徒歩で東京メトロ大手町駅に行った。
大手町からは、東西線で竹橋まで一駅だ。
東西線に乗る前に、地下のドトールで軽く食べた。
奥さんは、レタスドッグと黒ごまのマシュマロラテを注文し、
僕は、チーズのパンとオレンジジュースを頼んだ。
奥さんは、レタスドッグのソーセージをマシュマロラテに沈没させていた。

熊谷守一は、わりと好きな画家だ。
知っていたとおり、抽象化された、動物や風景の絵が多かった。
一見すると何の絵なのかわからないほど抽象化された絵の方が、
絵本のようにわかりやすくデフォルメされたものよりも、
より高尚な感じがしてよいと思った。
例えば、熊谷がシベリアで見たという乾いたような茶色い風景の中央に
赤や緑、白といった色の布のようなものが墓標のように描かれている
絵があった。
また、地味な茶色の画面で、ピンク色の亀が水から人間のペニスのように
顔をだしている絵や、これまた、おなじみの、茶色の画面で、
カッターで短く切れ込みをズッズッズッと入れたような
一本線の小魚の小群が泳いでいた。

そんな絵をずらずらと眺めた後、MOMATコレクションを見た。
ロバート・フランクの写真「青い空」に感動した。
モノクロ写真で窓の中の花を撮ったもので、その窓に
雲が写っていた。

また、チンポムのカラスが後ろから追っかけてくる映像作品が
とても印象的だった。
www.youtube.com

2017年の振り返り

2017年は、2017が素数というだけあって、良い年になるだろうと期待していた。しかし、僕にとっては、まあまあ普通の年であった。2017年は、数学を趣味にしようということで、数学の啓蒙書や数学者の自伝、高校~大学1、2年向けの本を読んだりしていた。神保町の書泉グランデに行って、ジョン・コーツ先生の講演を聞いたり、カルチャーセンターの代数幾何学の講義を受けたりした(仕事で疲れていて最初の1回しか行けなかった)。今までは、仕事に役立てようと、休日もプログラミングの勉強をしていた。それが、プログラミングの方の興味は少し減って、数学への興味が増したという感じだ。プログラミングはC言語C++言語の知識が少し増えたけど、まだまだだ。何か、自分なりのプログラムを書きたいな、と思っている。まあ、無理せずやっていこうと思う。

日記20171217

今日は、朝起きたら、パリパリの日だった。
パッキパキで、目の奥が痛くなるような青い空が、小さな窓を一色にしていた。
鉛の液体が脳、首、肩に詰まっていた。
急激な気圧の上昇による症状だと直感した。
いや、それは言い訳かもしれない。
週末の仕事の悩みが、次の日が月曜だという日曜の朝、鬱となって吹き出したのだろう。

数学の本を読み、ノートに計算を書いても、落ち着かなかった。
整域を同値類に分けて、体にすることができる。素イデアルによる剰余環は、整域であり、体ではない。
体にするには、極大イデアルが必要だ。しかし、整域という条件さえあれば、体は作れるのだな、
と考え、字を書いても、焦燥感が増すばかり。

確かなこと、価値ある事実がつかめていない気がする。
こんなときは、外に出て散歩をするに限る。
スーパーの地下にある1000円カットへ、散髪に行った。
「髪はすきますか?」
「はい。」
ハゲが丸見えになったので、失敗したなと思った。

コンビニでスパゲッティを買い、テレビ「ザ・ノンフィクション」を見ながら食べた。
そんな冬の一日だった。

Python3.6.3チュートリアル写経20171122

最近、寒いですね。

Pythonをかじっていて、お仕事で少し役立ったのは、リスト内包表記です。

テストケースを入力の組み合わせに応じて、作成するときに使えました。

for文でやっても同じですが、ネストが深くなります。

また、リスト内包表記の方が速い?

今日は、9.8. イテレータ~10.5. 文字列のパターンマッチング をやりました。

うーん、この辺は、やはり、何か具体的なプログラムを作りながら身につけるべきだと思います。

ちょっと、だれてきましたが、メモ。

  • イテレータを簡単に自作できるようにしたものがジェネレータ
  • ジェネレータのキーワードはyield
  • ジェネレータってschemeなどの「継続」と関係がある?
  • ジェネレータ式というのはリスト内包表記のメモリに優しい版で、丸括弧を使うところだけ違う

Python3.6.3チュートリアル写経20171119

9.3.1. クラス定義の構文~9.7. 残りのはしばし までやった。

  • classのコンストラクタは、__init__(self, arg1, arg2, ...)と書く
  • コンストラクタやメソッドの第一引数はselfと書く慣習
  • x.f() という呼び出しは、 MyClass.f(x) と厳密に等価
  • Pythonではクラスのメンバはすべてpublic
  • MyClass(MyBaseClass): のように括弧内に基底クラスを書くことによって継承を行う
  • クラス変数に、ミュータブルなオブジェクト(リスト、辞書など)を使用してはならない
  • メンバ変数とメンバメソッドは同じ名前のものを書くことができない。上書きされてしまう。
  • クラスを調べる組み込み関数 isinstance(obj, type) と継承関係を調べる組み込み関数 issubclass(MyClass, MyBaseClass) がある