継承したらポインタ使ってnewしないと代入できないぞ

やっとC++でやってた一連の作業が一段落した。
まぁまだあとちょっとありますけどね。


そんな中で気付いたこと。

class IFunc{
public:
virtual void operator()(void)=0;
}
class MyFunc : public IFunc{
//実装
}

こんな風にインターフェースを用意すると。
まぁC++にインターフェースという言葉はないだろうが、別にそれは問題ではないだろう。
ここで言うインターフェースというのはJavaなどで言うインターフェースと同じ機能を有するもののことだ。
=0と書けば継承先で必ず実装するメソッドになる。


さて、これを使うぞと思ってこんなコードを書いた。

IFunc f=MyFunc();
f();

結果はコンパイルエラー。IFuncをインスタンス化できませんとか内容だったと思う。
まぁこの辺がJava・C#とかやってた人が混乱するところだよね。
そもそもJavaではプリミティブ型以外、C#では参照型は参照で表されるわけだ。そして実体はnewで作る。
下で示すC++とC#のコードは実際的には似たようなものだと言うこと。

IFunc f=new MyFunc(); //C#
IFunc* f=new MyFunc(); //C++ (fの使用終了後delete必要)

実のところこれならば通る。
C++のクラスというのはCの構造体に毛が生えたものなので、変数自体が実体というC#の値型のようなこともできる。
C++ではどうしてもこの使い方が一般的になってしまう。だってnewで確保したらdeleteしないといけないから。
最初に書いたコードではそれをやろうとしているわけだ。
けどC#では値型である構造体はクラスに非常に似ているけど継承禁止になっている。
これは、T1構造体にはa,bというフィールドがあって、これを継承したT2構造体にはこれにcというフィールドを足した。
そしたらT2構造体の実体をT1構造体の変数に入れることはできませんよね。
C++ではこういうことができるのだけど、いざ代入してみようとすると上のようにひっかかる。
実にそういうことです。


じゃあどうしたかというと、newで確保するしかないでと。
とはいえ破棄のタイミングも難しいのでスマートポインタを使うことにした。
あと実際にやりたかったのはstd::vectorに詰め込むことなので……

std::vector<boost::shared_ptr<IFunc>> funcs;
funcs.push_back(boost::shared_ptr<IFunc>(new MyFunc());

あー、めんどくさい。


そういえばboostにboost::ptr_vectorとかいう変なコンテナがあるんだよね。
これはpush_backでポインタを投げ込んだらatではデリファレンスした結果が出てくると言うやつ。
そしてコンテナが破棄されるときに中身も破棄されると。
なんでポインタ投げ込んだのにポインタ出てこないのさ。というわけで僕はあんまり使っていない。
けどこれの意義って今みたいな例にあるのよね。
Let’s Boostの説明には「多態したいのでvectorではダメなのだけど(略)という状況でptr_vectorを使うという用途が想定されています。」と書かれている。
すなわち本音を言うとstd::vectorを使いたいがそれは叶わないと。
そんなときにboost::ptr_vectorを使っていただければ、
投げ込むときだけnewでやってもらえばあとはstd::vectorで思ってたとおりにいけるんじゃないでしょうかと。
そういうことかなと。なるほどね。しかしそれ以外使い道ないじゃないか。
ちょっと考えてみますわ。