いま、C++ プログラミングの筋と定石を読んでいる途中なんですが、未経験の実装が出てきて戸惑っているところ。
基底クラスのコンストラクタの中で、placement newを使って自分の領域(this
)を自分の派生クラスでconstructしてしまうというもの。これで、一種の仮想コンストラクタを実現してます。
new(this) Derived
というコードの賛否については保留したとして、それ以外で問題になるのがオブジェクトのサイズです。この仕組を実現しているのは基底クラスのコンストラクタの中なので、this
が指し示す領域のサイズが派生クラスのオブジェクトが収まるサイズなのかわかりません。というか、一般的にいって派生クラスでは拡張するのですから基底クラスのサイズの領域では収まらないはずです。
それを解消するために、operator new
をオーバーロードして、派生クラスのオブジェクトが収まるサイズの領域をあらかじめ確保する、ということをしています。
やりたいことはわかる。わかるし、以前書いたようにバイナリやストリームからオブジェクトを復元したいときにはたしかに有効な感じはします。
でも、これって、本当に、大丈夫、なんでしょうか?
以下コード。
#include <iostream> #include <sstream> #include <string> #include <stdexcept> // 基底クラスの宣言 class Base { public: void* operator new(std::size_t size); void operator delete(void* p); Base(std::istream& in); virtual ~Base(); virtual void write(std::ostream& out) const; protected: Base(); }; // 派生クラスの宣言: point class Point : public Base { public: Point(std::istream& in); void write(std::ostream& out) const; private: int x_; int y_; }; // 派生クラスの宣言: rectangle class Rectangle : public Base { public: Rectangle(std::istream& in); void write(std::ostream& out) const; private: int x_; int y_; int width_; int height_; }; // 派生クラスの宣言: circle class Circle : public Base { public: Circle(std::istream& in); void write(std::ostream& out) const; private: int x_; int y_; int r_; }; // 基底クラスの実装 void* Base::operator new(std::size_t) { // 本当はここで領域を割り当てるのだけれdも、今回は横着して固定領域を返す // (なので同時に複数のオブジェクトは扱えない) static char work[64]; std::cout << "Base::operator new" << std::endl; return work; } void Base::operator delete(void* p) { std::cout << "Base::operator delete" << std::endl; // 本当はここで Base::operator new で割り当てられた領域を解放するのだけれども // 今回は固定領域を使っているのでその手続きを省略 } Base::Base(std::istream& in) { std::string type; in >> type; if (type == "point") { ::new(this) Point(in); } else if(type == "rectangle") { ::new(this) Rectangle(in); } else if(type == "circle") { ::new(this) Circle(in); } else { throw std::runtime_error("unknown type"); } } Base::Base() { std::cout << "Base::Base" << std::endl; } Base::~Base() { std::cout << "Base::~Base" << std::endl; } void Base::write(std::ostream&) const { } // 派生クラスの実装: point Point::Point(std::istream& in) { in >> x_ >> y_; } void Point::write(std::ostream& out) const { out << "point " << x_ << " " << y_; } // 派生クラスの実装: rectangle Rectangle::Rectangle(std::istream& in) { in >> x_ >> y_ >> width_ >> height_; } void Rectangle::write(std::ostream& out) const { out << "rectangle " << x_ << " " << y_ << " " << width_ << " " << height_; } // 派生クラスの実装: circle Circle::Circle(std::istream& in) { in >> x_ >> y_ >> r_; } void Circle::write(std::ostream& out) const { out << "circle " << x_ << " " << y_ << " " << r_; } // Base 用挿入演算子 std::ostream& operator << (std::ostream& out, const Base& base) { base.write(out); return out; } // テスト int main(int argc, char* argv[]) { std::istringstream pointSource("point 10 20"); Base* p = new Base(pointSource); std::cout << "\t" << *p << std::endl; delete p; std::cout << std::endl; std::istringstream rectangleSource("rectangle 10 20 30 40"); p = new Base(rectangleSource); std::cout << "\t" << *p << std::endl; delete p; std::cout << std::endl; std::istringstream circleSource("circle 100 200 300"); p = new Base(circleSource); std::cout << "\t" << *p << std::endl; delete p; return 0; }
実行結果。
Base::operator new Base::Base point 10 20 Base::~Base Base::operator delete Base::operator new Base::Base rectangle 10 20 30 40 Base::~Base Base::operator delete Base::operator new Base::Base circle 100 200 300 Base::~Base Base::operator delete
- 作者: ジェームス・O・コプリエン,James O. Coplien,安村通晃,大谷浩司,渦原茂
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/12/01
- メディア: 単行本
- 購入: 2人 クリック: 20回
- この商品を含むブログ (5件) を見る