http://d.hatena.ne.jp/faith_and_brave/20091126/1259226643
// 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];
#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
// 参照型のインスタスをもつクラス 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
Hoge operator = (const Hoge& h) { memcpy(this, &h, sizeof(Hoge)); return *this; }
Hoge operator = (const Hoge& h) { this->~Hoge(); new (this) Hoge(h); return *this; }
#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; }