誕生日にもらった意外なもの

自前でnewのような関数を作っています。
mallocで確保したメモリ領域のそれぞれの項目に対してコンストラクタを呼び出したいのですが、
テンプレートで指定されたクラスのコンストラクタを明示的に呼ぶにはどうしたらよいですか?

テンプレート関数でなく、特定のクラスに対するものならできたのですが、
テンプレートで一般的に対応しようと思った所で躓きました。

また、deleteのほうは意図通り実装できました。


struct HOGE{
HOGE(){}
};

//特定のクラスに対するものは正常
HOGE *mynew(size_t len){
HOGE *v = (HOGE*)malloc(len * sizeof(HOGE));
for(size_t t = 0; t < len; ++t){
//v[t].HOGE();//error C2274: '関数形式のキャスト' : '.' 演算子の右側で使用できません。
v[t].HOGE::HOGE();//OK
}
return v;
}

template<typename T>
T *mynew(size_t len){
T *v = (T*)malloc(len * sizeof(T));
for(size_t t = 0; t < len; ++t){
v[t].T::T();//error C2039: 'T' : 'HOGE' のメンバーではありません。

//代わりに以下のように書いてみたが若干危険で効率も悪そう
T tmp = T();
memcpy(v + t, &tmp, sizeof(T));
memset(tmp, 0, sizeof(T));
}
return v;
}

template<typename T>
void mydelete(T *v, size_t len){
for(size_t t = 0; t < len; ++t){
v[t].~T();//OK
}
free(v);
}

HOGE *h = mynew<HOGE>(3);//NG
HOGE *j = mynew(3);//テンプレート関数でない方は呼べる
mydelete(j, 3);//OK

A 回答 (4件)

placement newを十分理解していないようですね。


C++で未初期化の領域に対して、明示的にコンストラクタを呼び出す方法は、placement new以外にはありません。

やりたいことは以下のようなものではないでしょうか

template<typename T>
T *mynew(size_t len){
T *v = (T*)malloc(len * sizeof(T));
for(size_t t = 0; t < len; ++t){
new(&v[t]) T();
}
return v;
}

この回答への補足

返事がおそくなってすいません。

なるほど、外部で確保したポインタを渡すことで、newをコンストラクタを呼び出すためだけに使うということですね。
newの構文が複雑なので、単にコンストラクタを呼ぶというテンプレート関数にして、mallocの部分と分離してしまった方がわかりやすそうです。

この方法をとってみようと思います。

補足日時:2011/06/27 17:09
    • good
    • 0
この回答へのお礼

placement newの記事を探しているうちに、当初の「渡されたポインタをそのまま返すnew」を知らずに、後から知られた使い方「任意の引数を渡せるnew」を先に知ってしまい、それを自分で定義しようとしてごちゃごちゃになっていたようです。

newをインクルードして教えていただいたコードが実行できました。

ありがとうございました。

お礼日時:2011/06/30 10:47

> テンプレート関数でなく、特定のクラスに対するものならできたのですが、



本当ですか?

> v[t].HOGE::HOGE();//OK

これは何をしているのか、ちゃんと理解していますか。

この回答への補足

ありがとうございます。

v[t]にたいしてコンストラクタを呼んでいるつもりなのですが、間違っているでしょうか。

補足日時:2011/06/15 11:36
    • good
    • 0

「これでおおよそ目的が果たせそうなのですが、operator new[]とoperator delete[]は定義できたのですが、operator newとoperator deleteはすでに定義されてるとでて、オーバーライド(?)できませんでした。


と書かれていますが, operator new や operator delete が突然出てきているのはなぜでしょうか?

元の質問を読む限り, 「グローバルのnewとdeleteを置き換える」必要などないはずですが.

いずれにしても, エラーメッセージは正確に書いてほしいし, 「どう書いたらどこでどのようなエラーが出たのか」を秘密にするようならまともな答えが返ってこない可能性は理解してほしい.

この回答への補足

placement newを調べるとoperator newにたどり着きました。

グローバルのnewとdeleteを置き換えたときに出たエラーはヘッダにかいて__inlineを付け忘れていたため、定義の重複でした。すいません。
void *operator new(size_t sz){ return malloc(sz + 1); }
error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) は既に inlineCheck.obj で定義されています。

グローバルなnewを置き換えたい理由は以下です。
1 要求された個数よりも1要素分余分なバッファを確保する
2 要求された個数分の要素に対して確保時にコンストラクタを、破棄時にデストラクタを呼ぶ。余分に確保された1要素分にはコンストラクタもデストラクタも呼ばず、0で埋める
3 intやコンストラクタを持たないオブジェクトは、未定義値ではなく0で埋める


グローバルな一般的なnewを置き換えるのは、ライブラリを使うときに混乱するので、いろいろ調べた結果、余分な引数を持ったnewがあることを知ったので、それをつかって目的が果たせそうです。
(通常のnewではvoidと要求されたサイズだけが渡され、1要素分のサイズがわからなかったが、余分な引数を持つnewでそのサイズが渡せた)


//配列の最後に要素0を追加して返すnull terminated new

void *operator new[](size_t tsz, size_t tsz2){
tsz += tsz2;
unsigned char *v = (unsigned char*)malloc(tsz);
for(size_t t = 0; t < tsz; ++t){
v[t] = 0;
}
return v;
}

void operator delete[](void *v, size_t tsz2){
free(v);
}

template<typename T>
T *ntnew(size_t tlen){
return new(sizeof(T)) T[tlen];
}

template<typename T>
void ntdel(T *v){
delete[] v;
}

補足日時:2011/06/15 12:07
    • good
    • 0

1st choice は placement new か.



あるいは適当に typename を追加する?

この回答への補足

ありがとうございます。
placement newについて調べた所、自前定義のnewが呼び出された後、勝手にコンストラクタを呼んでくれるようです。
また、自前のdeleteが呼び出される前に、勝手にデストラクタを呼んでくれるようです。

これでおおよそ目的が果たせそうなのですが、operator new[]とoperator delete[]は定義できたのですが、operator newとoperator deleteはすでに定義されてるとでて、オーバーライド(?)できませんでした。

グローバルのnewとdeleteを置き換える方法はありませんか?

補足日時:2011/06/09 16:21
    • good
    • 0

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