エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

virtual constructor...?

いま、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

C++ プログラミングの筋と定石

C++ プログラミングの筋と定石

  • 作者: ジェームス・O・コプリエン,James O. Coplien,安村通晃,大谷浩司,渦原茂
  • 出版社/メーカー: ピアソン桐原
  • 発売日: 2009/12/01
  • メディア: 単行本
  • 購入: 2人 クリック: 20回
  • この商品を含むブログ (5件) を見る