--/--/--

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2009/06/05

C++ の参照

C++の場合、リファレンスは初期化時のみ参照先の指定が可能で、 以降、参照先を変えることはできません。(ご存知の通り、)ポインタは アドレスの再代入により何度でも参照先を変えることができます。 既にポインタがあるので、リファレンスに上記のような制限が加えられて いるのだと解釈しています。

Ruby(Java)とC++のリファレンス(参照)の違い - エンジニア&Rubyistの徒然なるままに

できると思うんだけど。

#include <iostream>

class Foo {
    int x_;
public:
    explicit Foo(int x) : x_(x) { }
    int val() { return x_; }
};

int main(int argc, char** argv) {
    using namespace std;

    Foo f(1);
    Foo& fr = f;
    cout << fr.val() << endl;

    Foo g(2);
    fr = g;
    cout << fr.val() << endl;

    // fr = 0; // 無理

    return 0;
}

無理ってコメント入れてる行はコンパイルエラーになる。C++ のリファレンスは実体ありきだから、ポインタみたいに NULL ポインタを指させるようなことはできない。ポインタは "ある型の実体を指すアドレス値の型" だから、アドレスの代入によっていくらでも参照先を替えることができる。と思う。

2009/06/03

template とコピーコンストラクタ

参照カウント方式でポインタの破棄タイミングを管理するスマートポインタ、いわゆる shared_ptr を実装しててはまった。コピーコンストラクタはコピーコンストラクタで素直に用意しておきましょうという話。

はじめ、T 型に代入可能な U 型の shared_ptr を受け入れるコンストラクタを用意していた。つまりこんな感じ。

template<class T>
class shared_ptr
{
    template<class U>
    shared_ptr(const shared_ptr<U>& src);
};

もちろんこのコンストラクタの中では、カウントとポインタの共有、そして最も重要な参照カウントのインクリメントが行なわれる。T 型同士の時でも、なんとなく呼ばれそうだし、コピーコンストラクタの代用になりそうだ。しかし、これはまずい。

vector<shared_ptr<Foo> > v;
v.push_back(shared_ptr<Foo>(new Foo()));

一見問題ないように見えるけど、問題ありあり。というのも上で宣言した shared_ptr のコンストラクタが呼ばれなくて、デフォルトのコピーコンストラクタが呼ばれてしまう。普通のコピーコンストラクタが呼ばれれば、当然参照カウントのインクリメントもされない。参照カウントがインクリメントされなければ new した Foo も即刻破棄される。v には期待したオブジェクトではなく、 ラップした Foo が削除された、shared_ptr の残骸が残っているだけ。

じゃあどうすりゃいいのかと言うと、冒頭に述べたとおり、素直に普通のコピーコンストラクタを用意する。というかそもそもコピーコンストラクタってこのシグネチャ(値か参照か、const か非 const かは関係なかったような)しか認められないのかな。

template<class T>
class shared_ptr
{
    shared_ptr(const shared_ptr& src); // 追加
    template<class U>
    shared_ptr(const shared_ptr<U>& src);
};

2009/05/30

DataInput/DataOutput インタフェース

Java の DataInput/DataOutput に相当するインタフェースが、C++ (STL) やら C# (.NET) ライブラリにはなかったりする。もちろんそれらのライブラリで用意されている仕組みを使えば、同じものを実現することもできる。

データ IO するときに結構重宝してたもんで、用意されてるのが普通と思ってた。

2009/05/21

std::vector にポインタを突っ込む

最近の仕事はもっぱら不慣れな C++ を使っていて、ようやくコツを掴みはじめた感じ。値とポインタだけの C ならまだ分かりやすかったのだけど、これに参照が混ざった途端にやたらと混乱した。

値として受け渡しのできないクラスのインスタンスを STLコンテナに突っ込みたいときには boost の shared_ptr みたいなスマートポインタでラップするのが安全でいいよって話はよく見かける。じゃあ生のポインタを突っ込んだらどうなるのというお話。結果は予想通りだったのだけど、まぁやってみたという記録だけ。

ちなみにその予想とは、コンテナを値で受け渡しても、それに内包されているポインタたちは値コピーされるだけ。ポインタが指してるオブジェクトの実体は、何も変わらない。shared_ptr ならコンテナが値渡しされるタイミングで、自身もコピーコンストラクタに渡される。この時(参照カウント方式で shared_ptr が実装されていれば)参照カウントが +1 され、渡した先のブロックが終了したタイミングで参照カウントが元に戻る。で、自分のブロックが終わったタイミングでカウントが 0 になり、オブジェクトの実体も破棄される(という仕組みだと思ってる)。

以下実験のコード。

#include <vector>
#include <iostream>

using std::vector;
using std::cout;
using std::endl;

class X {
public:
    X() { cout << "Default Constructor" << endl; }
    X(const X& x) { cout << "Copy Constructor" << endl; }
    X& operator=(const X& x) { cout << "op=" << endl; return *this; }
    ~X() { cout << "Destructor" << endl; }
};

void withValue(vector<X*> x) { cout << "withValue" << endl; }
void withRef(vector<X*>& x) { cout << "withRef" << endl; }
void withPtr(vector<X*>* x) { cout << "withPtr" << endl; }

void withValue(vector<X> x) { cout << "withValue" << endl; }
void withRef(vector<X>& x) { cout << "withRef" << endl; }
void withPtr(vector<X>* x) { cout << "withPtr" << endl; }

int main() {
    cout << "vecotr<X*>" << endl;
    vector<X*> xps;
    xps.push_back(new X());

    withValue(xps);
    withRef(xps);
    withPtr(&xps);

    vector<X*>::iterator i;
    for (i = xps.begin(); i != xps.end(); ++i) {
        delete *i;
    }

    cout << endl << endl;

    cout << "vecotr<X>" << endl;
    vector<X> xs;
    xs.push_back(X());

    withValue(xs);
    withRef(xs);
    withPtr(&xs);

    return 0;
}

結果。

$ ./a.out
vector<X*>
Default Constructor
withValue
withRef
withPtr
Destructor


vector<X>
Default Constructor
Copy Constructor
Destructor
Copy Constructor
withValue
Destructor
withRef
withPtr
Destructor

原則、生のポインタを STL コンテナに突っ込むようなことはしないほうがいいのだろうけど。shared_ptr が使えないとか、shared_ptr を自前で実装するのが面倒なときなんかは気をつけて使ったりするといいんじゃなかろーか。いやよくはないか。

2009/05/15

ubuntu で省電力化 - powernowd

powernowd てのをインストール (apt-get install) すると、負荷に応じて CPU のクロック数を上げ下げしてくれる。CPU の温度がひどいことになっていたのだけど、だいぶ改善した。

Linux/BSD で違ったり、Linux の中でもディストリごとに違ったりするのだけど、大体このようなことをしてくれるアプリケーションは用意されているみたい。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。