[[C++ Tips]] * boost pool [#s2f524fb] - boost poolには、pool, object_pool, singleton_poolの3種類のタイプがある - pool -- malloc/freeを行う -- コンストラクタでサイズを指定 -- コンストラクタで指定サイズのメモリをアロケート/フリーできる -- 指定サイズのN倍も可能 (ordered) -- orderedと通常がある。 - object_pool -- クラスのインスタンスを生成/破棄する -- orderedのみ - singleton_pool --サイズ別にシングルトンとして動作する。 --シングルトンとして使える。 --vectorなどSTLのコンテナのアロケーターとして使える --メモリの解放にはpurge_memoryしかない。(全放棄) ** orderedとは [#vc6ee3bd] - 必ずメモリアドレスが順番になる(連続する) - orederdだと、破棄したメモリブロックの再利用はされない - 解放するには、release_memoryを呼ぶ(アロケートブロックが解放される) -非orderedが一つでも混在していると、release_memoryができない - おそい **通常版(非orederd) [#ha2b1cac] -破棄したメモリブロックが再利用される -release_memoryはできない -全部破棄のpurge_memoryは可 -連続したメモリブロックを確保できない *** ordered、非orderedともに一長一短で使いにくい [#r8f6dbcf] * オーバーライドした関数の戻り値は、基本クラスの定義の戻り値を継承したものでもOK [#rdf3f287] #include <stdio.h> struct Base { virtual Base* create() = 0; // create new instance virtual Base* clone() = 0; // create and copy instance virtual void print() = 0; }; struct Sub : Base { Sub() {} Sub(const Sub& sub) {} // copy constructor Sub* create() { return new Sub(); } Sub* clone() { return new Sub(*this); } void print() { printf("I am Sub\n"); } }; int main(int ac, char* av[]) { Base* base1 = Sub().create(); Base* base2 = base1->clone(); base2->print(); return 0; } - 実行結果~ http://t.co/ASDjAEvI * boost:multiindexは便利 [#md6c228e] http://d.hatena.ne.jp/faith_and_brave/20091126/1259226643 * virtualやポインタを使わず、サブクラスの生オブジェクトをコンテナに入れるためのクラス [#d948f293] // BaseClassのサブクラスをコンテナにいれるためのクラス // このクラスを使うことで、ポインタを使わず動的ポリモーフィズムを実現する template <typename BaseClass, size_t MaxSize> struct DynamicPolymorphism : BaseClass { uint8_t body[MaxSize - sizeof(BaseClass)]; DynamicPolymorphism() : BaseClass() {} DynamicPolymorphism(const DynamicPolymorphism& m) : BaseClass(m) { memcpy(body, m.body, sizeof(body)); } template <typename SubClass> DynamicPolymorphism(const SubClass& m) : BaseClass(m) { SubClass& self = reinterpret_cast<SubClass&>(*this); self = m; } template <typename SubClass> void operator = (const SubClass& m) { SubClass& self = reinterpret_cast<SubClass&>(*this); self = m; } template <typename SubClass> operator SubClass& () { return reinterpret_cast<SubClass&>(*this); } }; ※これでunionから開放されます 比較的サイズが小さいクラスを実体のままコンテナに格納する場合、継承したクラスオブジェクトを入れる事が難しい。手っ取り早い方法としては、unionを使う事があるが、unionを使うとコンストラクタが書けないなどのC++11以前の制約がある。 例 class Car { int cylinders; /* 気筒数*/ int displacement; /* 排気量 */ }; class Track : public Car { int payload = 1000; } // 最大積載量 class Bus : public Car { int maxPeoples = 30; } // 乗車定員 vector<Car> carList; carList.push_back(Truck()); // NG TruckがCarでスライシングされてpayloadが失われる carList.push_back(Bus()); // NG BusがCarでスライシングされてmaxPeoplesが失われる vector<Car*> carList2; carList2.push_back(new Truck()); // OK Truckの情報も保持される carList2.push_back(new Bus()); // OK maxPeoplesの情報も保持される ポインタを使えば解決するけど、小さいオブジェクトを全部newで作るのは効率が悪い。 unionを使った解決方法 class AllCar { enum Type { Truck, Bus }; Type type; int cylinders; int displacement; union { int payload; int maxPeoples; }; }; vector<AllCar> allCarList; AllCar truck; truck.type = AllCar::truck; allCarList.push_back(truck); DynamicPolymorphismを使った例 class Car { enum Type { Truck, Bus }; Type type; int cylinders; /* 気筒数*/ int displacement; /* 排気量 */ }; }; class Track : public Car { int payload = 1000; } // 最大積載量 class Bus : public Car { int maxPeoples = 30; } // 乗車定員 typedef DynamicPolymorphism<Car, max(sizeof(Truck), sizeof(Bus))> AllCars; vector<AllCars> allCars; allCars.push_back(Truck()); // AllCarsは継承クラスの最大サイズが確保されているのでスライシングは起こらない allCars.push_back(Bus()); Truck& t = allCars.front(); // DynamicPolymorphismのオペレーターでTruckに変換される Bus& t = allCars[1]; * operator new の効力範囲 [#tdb48b30] #include <stdio.h> #include <stdlib.h> void* operator new (size_t sz) throw() { printf("global operator new\n"); return 0; } int main(int ac, char* av[]) { struct newtest { static void* operator new (size_t sz) throw() { printf("newtest operator new\n"); return 0; } void dotest() { char* a = new char; printf("a = %p\n", a); } }; newtest t; t.dotest(); newtest* t2 = new newtest; }; 実行結果 global operator new a = (nil) newtest operator new * 参照型のメンバをもつクラスのコンテナとソート [#j00f0661] ** 参照型のインスタンスをもつクラスは、通常だとvector等のコンテナに格納できない。 [#k54c94c3] // 参照型のインスタスをもつクラス struct Hoge { int& instance_; Hoge(int& i) : instance_(i) {} Hoge(const Hoge& h) : instance_(h.instance_) {} bool operator < (const Hoge& h) const { return instance_ < h.instance_; } }; std::vectorに入れてみる std::vector<Hoge> vectorhoge; int h1 = 1; vectorhoge.push_back(h); // コンパイルエラー operator =がないとvector::push_backはできない listならOK std::list<Hoge> listhoge; int h1 = 1; listhoge.push_back(h); // OK ** vectorに格納可能なリファレンスをもつクラスの定義 [#vea9f3c7] - vectorに格納(push_back, back_insert_iteratorなど)には、operator = が必要 - リファレンス型の変数は、リファレンス情報の更新はできない。代入もswapも参照先のデータにたいして行われる。 *** 禁断の技その1 [#m5f8dfc7] Hoge operator = (const Hoge& h) { memcpy(this, &h, sizeof(Hoge)); return *this; } + thisのデストラクタが呼ばれないで破棄される + オブジェクトのバイナリーコピーは非常に危険なのでダメ *** 禁断の技その2 [#f178b88a] Hoge operator = (const Hoge& h) { this->~Hoge(); new (this) Hoge(h); return *this; } + いちおう、言語的には安全。 + ハーブ・サッターさんは絶対に使うなと警告している方法 + コピーコンストラクタとデストラクタで例外を投げないことが前提。例外が発生すると破綻する。 + スレッドセーフでもない。シングルスレッド限定。 + 自己代入の処理も必要 + 欠点は多いけど、これでvectorに対する要素の追加やsortが可能になる *** コードの例 [#s7418473] #include <cstdio> #include <list> #include <vector> #include <algorithm> #include <functional> #include <iostream> // 参照を持つクラスのswap namespace { struct Hoge { int& instance_; Hoge(int& i) : instance_(i) {} Hoge(const Hoge& h) : instance_(h.instance_) {} // コピーコンストラクタとnewの第二構文を使った初期化を使って実現する。 // 例外安全ではない。デストラクタ・コピーコンストラクタが例外を投げない // スレッドセーフではない。スレッドセーフにするには排他処理が必要。 Hoge& operator = (const Hoge& h) { if (this != &h) { // 自己代入でない this->~Hoge(); // thisを破棄 デストラクタは例外を投げない // ここで他のスレッドがthisを参照すると破綻する。シングルスレッド限定! try { new(this) Hoge(h); } catch (...) { assert(false); // 例外が投げられたら停止 } } return *this; } // リファレンスタイプのインスタンスは通常の方法ではswapできないので、 // コピーコンストラクタとnewの第二構文を使った代入の=を使って実現する。 // 例外安全ではない。コピーコンストラクタが例外を投げないことが前提 void swap(Hoge& h) { Hoge tmp(h); // hをコピー h = *this; *this = tmp; } bool operator < (const Hoge& h) const { return instance_ < h.instance_; } }; // output用 void print(const Hoge& h) { printf("%d\n", h.instance_); } } int main(int argc, char* argv[]) { int i1 = 1; int i2 = 2; int i3 = 3; Hoge h1(i1), h2(i2), h3(i3); std::vector<Hoge> vectorhoge; vectorhoge.push_back(h3); // operator =がないとvector::push_backはでき ない vectorhoge.push_back(h2); vectorhoge.push_back(h1); std::back_inserter(vectorhoge) = h1; std::sort(vectorhoge.begin(), vectorhoge.end()); // operator =がないと vector::sortはできない std::cout << "vector sort" << std::endl; std::for_each(vectorhoge.begin(), vectorhoge.end(), print); std::list<Hoge> listhoge; listhoge.push_back(h3); listhoge.push_back(h2); listhoge.push_back(h1); std::back_inserter(listhoge) = h1; listhoge.sort(); std::cout << "list sort" << std::endl; std::for_each(listhoge.begin(), listhoge.end(), print); return 0; } * [[C++OPTIONAL]] [#beb6d77b]