アプリ版:「スタンプのみでお礼する」機能のリリースについて

ちょっと長文です。下のほうに質問があります。

STLのvectorコンテナを使用しているのですが、
困った事態が発生しています。

自分は、参照型を要素として、持たしたいのですが、
持たすことができません。
なにやら調べてみたところ、STLのコンテナクラスというのは
基本的に「値ベースのコンテナ」らしく、「参照ベースのコンテナ?」
としてコンテナを使うには、ポインタ型を格納して下さい。との
ことでした。
ただし、この方法は2重deleteが発生する危険性を孕んでいるので、
Boostのなんかのポインタークラス?のようなものを使えば、
そのような問題に悩まされることないですよー。とありました。

ここで問題なのは、
・Boostを扱えるだけの知識がない。
・そもそもBoostを使えるまで環境設定できる自信がない。
ということです。
そのため、普通のポインタを使って実装しようと思うのですが、
そして上記のような問題が出てくるにつれ自分の中では
次のような疑問点が出てきました。
●質問(1)
・なんで参照型を格納できるコンテナがないのよ!
 本当はあったりするんだけども、自分が知らないだけ?
●質問(2)
・関数間でオブジェクトを渡すときには、パフォーマンスとかも
 考慮してもconstキーワードを使いつつの参照渡しがよい。と
 Effective C++か何かで読んだのですが、コンテナに格納する
 場合にはこれは有効ではないのか?
 また、オブジェクトは、基本的には、やたらめったら
 コピーするものではなく、一つオブジェクト用のメモリ領域を
 作ったらそれを流用(ポインタ・参照を使って参照する)した方が、
 作り的にきれいな気がするのですが、なにか方針とかあったりする
 のでしょうか?

以上長くなってしまいましたが、よろしくお願いいたします。

A 回答 (1件)

 こんばんは。



●質問(1)
 STLコンテナの内部にある

 typedef typename T& reference;

 で、Tの形が何かしらの参照であった場合、

 typedef typename T&& reference;

 となり、コンパイルエラーになります。
 何故参照を出来るコンテナが無いのかは私にも分かりません。飽くまで推測ですが「想定外」だったのではないでしょうか。
 TR1のリファレンスラッパーは正しく参照をコンテナにしまう為の物ですが、随分と大袈裟な代物です。
 http://www.aristeia.com/EC3E/TR1_info.html

 これ位ならば、自身で作成してしまった方が手っ取り早いでしょう。

●質問(2)
 当然、オブジェクトがコンテナよりも寿命が長いのであるならば参照も有効でしょう。
 しかしそれよりは動的に作成したオブジェクトのポインタを入れる事の方が良く有る様です。

 std::vector<XXX*> v(1, new XXX());

 しかし↑の状態では「v」が別の場所に複写され(v2とする)「v2」が「v」よりも寿命が長い場合、XXX*をdeleteする責任は「v2」の方に発生します。
 ここで間違えて「v」側でXXX*にdeleteをかけてしまうと、それよりも寿命の長い「v2」で「消去済み」のポインタに触る事になります。
 その為に、参照カウント式のスマートポインタ等を使用したりします。

 std::vector<smart_ref_pointer<XXX> > v(1, smart_ref_pointer<XXX>(new XXX()));

 参照カウント式のスマートポインタは、コピーや代入に反応してカウントを上げて行き、デストラクタでカウントを下げます。寿命の問題に呪われる事の多くは回避出来ます。

 以下は簡単なリファレンスラッパーです。

#include<vector>

struct CVoidPtrBase
{
protected:
explicit CVoidPtrBase(const CVoidPtrBase& r) : m_p(r.m_p){}
explicit CVoidPtrBase(const void* p) : m_p(const_cast<void*>(p)){}
CVoidPtrBase& operator=(const CVoidPtrBase& r)
{
if(this != &r)new (this) CVoidPtrBase(r);
return *this;
}
void* get(){ return m_p; }
const void* get() const { return m_p; }
private:
void* m_p;
};

template<class __TP>
struct CRefWrapper : private CVoidPtrBase
{
typedef typename CRefWrapper<__TP> _Self;
typedef typename __TP value_type;
typedef typename __TP* pointer;
typedef typename __TP& reference;
typedef typename const __TP* const_pointer;
typedef typename const __TP& const_reference;

CRefWrapper(const _Self& r) : CVoidPtrBase(r){}
explicit CRefWrapper(const_pointer p) : CVoidPtrBase(p){}
explicit CRefWrapper(const_reference r) : CVoidPtrBase(&r){}
_Self& operator=(const _Self& r)
{
if(this != &r)CVoidPtrBase::operator=(r);
return *this;
}
operator reference(){ return *get(); }
operator const_reference() const { return *get(); }
private:
pointer get(){return static_cast<pointer>(CVoidPtrBase::get());}
const_pointer get() const { return static_cast<const_pointer>(CVoidPtrBase::get()); }
};

struct CTest
{
explicit CTest(int i) : m_i(i){}
CTest(const CTest& r) : m_i(r.m_i){}
CTest& operator=(const CTest& r)
{
if(this != &r)new (this) CTest(r);
return *this;
}
private:
int m_i;
};

//お試しと確認
int main()
{
CTest test(5);
::printf("%p\n", &test);

std::vector<CRefWrapper<CTest> > v(1, CRefWrapper<CTest>(test));

const CTest& refTest = v[0];
::printf("%p\n", &refTest);

CRefWrapper<CTest> refWrapper = v[0];
::printf("%p\n", &static_cast<CTest&>(refWrapper));

CTest test2(150);
::printf("%p\n", &test2);

const CRefWrapper<CTest> refWrapper2(test2);
::printf("%p\n", &static_cast<const CTest&>(refWrapper2));

refWrapper = refWrapper2;
::printf("%p\n", &static_cast<CTest&>(refWrapper));
return 0;
}
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
頂いたソースですが、
Borland C++ Compiler 5.5でも、codepadのおそらくgccでも
コンパイルエラーが出てしまいました。。
Microsoft Visual Studio 2005でならコンパイルできましたが、
現時点の自分の読み取り能力ではちょっと解読できませんでした><

どのような場合に生のポインタを用いたコンテナを使ったときに
問題になるのかをもう一度整理してみようと思います。
また参照カウント式のスマートポインタを使ったときに、
内部がどのような動きになるのかについても考えてみます。

その上でどうしても今作ろうとしているものに参照カウント式の
スマートポインタを使ったほうがよいのかを判断しようと思います。

お礼日時:2008/10/01 23:18

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!