春鳥 -ハルドリ-

2012年7月~12月

2012-11-08(THU)

かけるよ

Rubyでいうところのコレ↓

"hello!" * 3 #=> "hello!hello!hello!"

str = "world!"
str * 3 #=> "world!world!world!"

ary3 = [1, 2, 3] * 3 #=> [1, 2, 3, 1, 2, 3, 1, 2, 3]

ary6 = ["?"] * 2 * 3 #=> ["?", "?", "?", "?", "?", "?"]

をC++で。

#include "stdafx.h"
#include <string>
#include <vector>
#include <boost/assign.hpp>
#include <boost/range/algorithm_ext.hpp>

template<typename Container>
Container operator*(const Container& source, int num)
{
    if(num < 0)
    {
        throw std::invalid_argument("negative argument");
    }

    Container result;
    for(int i = 0; i < num; ++i)
    {
        boost::insert(result, std::end(result), source);
    }
    return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
    using namespace boost::assign;

    std::string("hello!") * 3; // "hello!hello!hello!"

    std::string str = "world!";
    decltype(str) str3 = str * 3; // "world!world!world!"

    std::vector<int> vec = list_of(1)(2)(3);
    decltype(vec) vec3 = vec * 3; // [1, 2, 3, 1, 2, 3, 1, 2, 3]
    
    std::list<std::string> lst = list_of("?");
    decltype(lst) lst6 = lst * 3 * 2; // ["?", "?", "?", "?", "?", "?"]

    return 0;
}

そういえば、Rubyには符号なし整数を表すクラスってないんですね。なので、C++の方でもRubyの動作にあわせて、負数との掛け算を行った場合は例外を投げるようにしています。

2012-08-27(MON)

脱ヒキ

わんくま同盟 横浜勉強会 #04に参加してきます。極度の人見知りが、知らない人の集まる場所に行って、あまつさえ懇親会に出席するという無謀さ。だ、だ、大丈夫さ……た、多分。

一応、その界隈の人のBlogや書籍は読んでるから、話題にはついていけると思うけれど、会話ができるかどうか不安ですね。

ぷらぷらー

というわけで、僕がC++erであることをアッピールするために、今更ながらC++プログラマであるかを見分ける10の質問に答えてみました。微妙な間違いを含んでいる可能性があるので、話半分で読んでくださいな。

  1. iterator の役割について説明せよ.

    STLを始めたばかりの人間を混乱と恐怖に陥れる役割。begin?end?何これ怖い。

    STLコンテナとSTLアルゴリズムの橋渡しをする役割。STLの要。

    アルゴリズムは、イテレータを介してコンテナにアクセスすることにより、コンテナのデータ構造を意識する必要がなくなる。逆にコンテナは、適用先のアルゴリズムについて何も知らなくてよい。これがトンでもない拡張性を生み出す。また、イテレータアダプタを使えば、STLコンテナコンテナ以外のモノにもSTLアルゴリズムを適用できるし、イテレータトレイツのおかげで、ポインタをランダムアクセスイテレータとして扱うこともできる。ほら、便利。

  2. *_cast およびCスタイルのキャストそれぞれについて概要を説明せよ.

    static_castは、普通の型変換に使うキャスト。
    dynamic_castは、ダウンキャストのときに使うキャスト。キャストに失敗したときの動作が、ポインタのときと参照のときで違うから注意しよう。
    const_castは、cv修飾子を付けたり、外したり、そのままにしたり(?)できるキャスト。やむを得ない理由でconstを外すために使われることがほとんど。
    reinterpret_castは、数値をポインタとして認識させたり、その逆をしたりするときに使うキャスト。でも、環境依存だから使っちゃだめだよ、ってMeyersが言ってた。
    *_cast全体の話としては、「<>」を使っているおかげで、テンプレート関数と見た目が同じっていう特徴がある。なのでBoostには、lexical_castやpolymorphic_downcastっていうキャストのように使えるtemplate関数がある。
    Cスタイルのキャストは、Cとの互換性のためだけにあるキャスト。でも、他人の書いたC++コードを読むと9割9分コレが使われているという、悲しい現実がある。

  3. overload と override と hiding の違いについて説明せよ.

    overloadは、名前が同じでシグネチャの異なる関数を定義すること。C++ではoperatorのoverloadもできる。戻り値型だけが違うoverloadは書けないから、そういうときはtemplate関数を使おう。
    overrideは、基底クラスでvirtualを付けて定義している関数と同じ名前、同じシグネチャを持つ関数を派生クラスで定義すること。実行時の多態性はこの機能を使って実現する。
    hidingは、基底クラスで定義済みの名前を派生クラスでうっかり再定義してしまうこと。これをやるとコンパイラが警告を出してくれるはず……と思ったらVC++2012 RCでは出なかった。マジかよ。なお、::(スコープ解決演算子)を使うことで、隠してしまった名前を参照することができる。

  4. const の機能について概要を説明せよ.

    僕の大好きな機能……などと言っているとconst厨と呼ばれるから気をつけよう。

    あるオブジェクトがコード中で不変であることを表現するときに使う。ただし、const_castで外せてしまうから、この不変性は完全ではない。オブジェクトを引数にするとき、const参照という形でよく使う。

    その他の使い方としては、「constメンバ関数」というメンバ変数を変更しないメンバ関数を定義するときに使う。ただし、mutableがついているメンバ変数は変更可能。

  5. 多重継承について概要を説明せよ.

    複数のクラスを基底クラスに持つクラスを定義すること。多段継承ではないので注意しよう。

    菱形継承問題とか、両方の基底クラスに同じ名前のメンバがいる場合とか、ややこしい事態になる場合があるので、肯定派と否定派がいるらしい。

    C#ではできない。Javaでもできない。でも、C++はできる。それだけ。

  6. ポインタの使用方法について,メモリーリーク問題等と絡めながら戦略を述べよ.

    灰は灰に、塵は塵に。newしたらdelete、mallocしたらfree。

    注意してても、忘れることだってあるさ。にんげんだもの。と、いうわけで、素直にスマートポインタを使おう。また、動的に配列を確保するなら、STLコンテナを使おう。

  7. コピーコンストラクタおよび代入演算子の扱いにおける戦略について述べよ.

    どちらも、(必要であれば)コンパイラが自動的に書いてくれるモノ。けれど、ポインタをメンバに持つ場合は「浅い」コピーになるので、「深い」コピーが必要なら自分で実装しよう。

    「コピー」操作をしないなら、privateに関数宣言だけ書いて、コピー不可であることを表現しよう。Boostが使えるなら、boost::noncopyableをprivate継承させてもいいんじゃないかな。

  8. virtual デストラクタの概要および使用上の戦略について述べよ.

    クラスを多態的に扱う場合、デストラクタが非virtualだと、オブジェクトが中途半端に破棄されて面倒なことになる。なので、多態的に扱われる基底クラスのデストラクタは必ずvirtualにしよう。

    あと、メンバ関数がすべて非virtualのクラスを抽象クラスにしたいときは、デストラクタを純粋仮想関数にするとよい。

  9. コンストラクタ,デストラクタにおける例外処理についての戦略を述べよ.

    コンストラクタでエラーが発生した場合、戻り値でエラーを返すことはできないから、例外を投げるしかない。また、デストラクタが呼ばれないから、リソースはRAIIの考え方が使われているクラスに管理させておこう。

    デストラクタでは、例外をthrowしてはならない。例外発生中にデストラクタが例外をthrowすると、二重例外になり、terminate()が呼び出されプログラムは直ちに終了する。

  10. 抽象クラスとテンプレートクラスの使い分けについてインターフェースと言う観点から述べよ.

    これだけ、答え方が分からない。そもそも、使い分けるもの、なの?

    うーん、抽象クラスは純粋仮想関数の実装を派生先に強制でき、テンプレートクラスはCRTPを使うことができる、とか?

以上、お疲れ様でした。

2012-07-14(SAT)

すまほか

ケータイをスマホに変えました。機種は、SO-03D

使い道は外出中のネット利用が主です。スマホでもブラウザは、Opera(Mobile)を愛用しています。ネットがいつでも手の中にある、っていうのは良いものですね。あと、地図アプリが方向音痴の自分にはすごくありがたいです。自分が向いている方向まで表示してくれるとは……、すごい時代になったものだ。

Web本棚

ブクログというWebサービスを見つけたので、持っている本(とBD)を登録してみました。

「技術書」に分類している中でおススメの1冊をあげるとすれば、Effective C++ですかねぇ。ススメられるのは、C++プログラマ限定ですけど。

個人的にこの本の中で一番重要だと思うのは、第1項の「C++を複数の言語の連合とみなそう」です。世の中には、CみたいなC++コードと、JavaみたいなC++コードが多すぎます。たまにはSTLとtemplateのことも思い出してください……。