生ポインタをC++で使うのが不安で仕方ないのでスマートポインタを勉強しているのだが、unique_ptr周りの挙動で独特な部分があったのでまとめておこうと思う。
#ポリモーフィックな定義をするとき
C++でポリモーフィックなコードを書くときは、次のように親クラスのポインタに子クラスをnewで代入することが多い。
parent_class* myptr = new child_class;
ただし誰もが知るように大原則としてnewしたら必ずdeleteしなければならないので、この方法ではうっかりdeleteし忘れる不安がついて回る。
そこでポインタの代わりにスマートポインタを使う。これはC++11以降導入された機能で、スコープを抜けるとき勝手にデストラクタを呼んでくれる便利な代物だ。使うにはmemory
ライブラリをincludeしておき、さらにコンパイルオプションに-std=c++11
(c++11以降ならよい)と指定する必要がある。
std::unique_ptr<parent_class> myptr(new child_class);
このようにしておくことで、myptr
がスコープを抜けて消滅するときデストラクタが呼ばれる。通常のポインタと同様まずはparent_classのデストラクタが呼ばれるし、それがvirtualならchild_class、parent_classの順でデストラクタが呼ばれる。
#インスタンスの配列なら?
Cを知ってる人ならわかるが、配列を指す生ポインタは配列の先頭アドレスを指す。そのため生ポインタでは次のようにすることでインスタンスの配列をアロケートすることができる。
parent_class* myptr = new child_class[10]
//...
//deleteするとき
delete[] myptr
また、組み込み型に限り、スマートポインタでも同様に置き換えることができる。
std::unique_ptr<int[]> myint[10]
なので、インスタンスの配列でも同じようにしたくなるところだが、うまくいかない。
//コンパイルが通らない
unique_ptr<parent_class[]> myptr(new child_class);
unique_ptr<parent_class[]> myptr(new child_class[10]);
//コンパイルは通るがresetできない
unique_ptr<parent_class[]> myptr(child_class[10]);
そこで調べたところ、以下の記事にたどり着いた。
どうやらunique_ptrでインスタンスの配列の先頭アドレスを指すというような使い方はできないようで、無理にunique_ptrを使おうとしてもなかなか面倒なようだ。
なのでこのような場合はおとなしくvector<parent_class>
やarray<parent_class>
とするのがよいようだ。しかしこれではポリモーフィズムを実装できない。
そこで、unique_ptrのコンテナという形で定義する。arrayを使う場合は次のようになる。
std::array<std::unique_ptr<parent_class>, 10> myptrs;
for(int i=0;i<10;i++){
myptrs[i].reset(new child_class);
}