Managed Memory Segmentsで動的多態する場合の留意事項
boost.ipcのManaged Memory Segmentsで動的多態を演じたい場合、destroy(_ptr)を呼び出す際、作成時の型を指定する必要があります。つまり抽象クラスの型を指定して、抽象クラスのポインタを渡して、削除することはできません。
namespace ipc = boost::interprocess; struct base { virtual ~base() {}; virtual void foo()=0; }; struct derive : public base { ~derive() {}; void foo() {}; }; ipc::managed_heap_memory heap(1024); base* ptr = heap.construct<derive>(ipc::anonymous_instance)(); heap.destroy_ptr(ptr); // コンパイルエラー、抽象クラスをインスタンス化しようとした
また、抽象クラスで無ければいいのか、つまり仮想関数を持ったクラスなら良いのかというと実行時にアクセス違反になります。
struct base { virtual ~base() {}; virtual void foo() {}; }; struct derive : public base { //先ほどと同じ int n; // なぜつけるかは後述 }; ipc::managed_heap_memory heap(1024); base* ptr = heap.construct<derive>(ipc::anonymous_instance)(); heap.destroy_ptr(ptr); // コンパイルは通るがランタイムエラー
つまり、デリータによって自動削除させる場合はあきらめてconstruct時の型情報を持って、destroy_ptrをよばないといけない訳です。しかし、どうやってデリータにconstructでの型情報を持たせたまま、unique_ptrの型はデリータの型に影響しないようにするのか、というのはちょっと難しい。この場合はあきらめてshared_ptrを使うのが無難で確実だと思います。
上記で変数nをderiveに追加しているのは、基底クラスとサイズが同じ場合無事削除されてしまうからです。詳しくは[追記]をご覧下さい。
// Managed Memory Segmentsはデリータを持っているのでそいつを使う // 面倒なので、anonymous_instanceとして作成するヘルパ template<class T, class Base, class ManagedSegments> boost::shared_ptr<Base> make_anonymous( ManagedSegments & seg ) { BOOST_STATIC_ASSERT( !std::is_rvalue_reference<ManagedSegments>::value ); return boost::shared_ptr<Base>( seg.template construct<T>(ipc::anonymous_instance)() , typename ManagedSegments::template deleter<T>::type(seg.get_segment_manager())); } const auto before = heap.get_free_memory(); { auto i = make_anonymous<derive,base>(heap); BOOST_ASSERT(before != heap.get_free_memory()); } const auto after = heap.get_free_memory(); BOOST_ASSERT(before == after);
※ BOOST_STATIC_ASSERTの引数間違えてました。
はい。とっても面倒ですね。動的多態をしたい場合はこんなことしなきゃいけないですが、普通に型をそのまま扱っている場合はこんな面倒な事しなくても良いです。unique_ptrも使えますし。動的多胎を考えている場合はちょっとひと工夫したり、うまく使わないと面倒になりそうです。
# 上のmake_anonymousですが、引数も渡せるようにしたいですね。・・・VS10にvariadic templates欲しい。
[追記] 同じサイズならば問題ないか?
上記の記述を見るに、仮想関数を持つクラス(純粋仮想関数は持たない)を継承したクラスが基底クラスと同じサイズならばどうなんでしょうか。
struct base { virtual ~base() { std::cout << "base" << "\n"; }; virtual void foo() {}; }; struct derive : public base { ~derive() { std::cout << "derive" << "\n"; }; void foo() {}; }; BOOST_STATIC_ASSERT( sizeof(base) == sizeof(derive) ); base* p = heap.construct<derive>(ipc::anonymous_instance)(); heap.destroy_ptr(p); // ----- 出力結果 derive base
基底クラスが抽象クラスでなく、サイズが同じならチェックを通って、無事削除されるようです。これって大丈夫だったっけ。ちょっと不安だし、サイズが同じ状況ってあんまりないだろうから知ってても微妙なところです。動的多態の場合は本文に書いたような方法の方が良さそうですね。