class BulletHolder
{
private :
float x, y ;
public :
virtual void update() = 0 ;
virtual BulletHolder * construct_copy(void * ptr) const = 0 ;
virtual ~BulletHolder() { }
} ;
#define BULLET_MAKE_CONSTRUCT_COPY_PP(TYPE) \
virtual BulletHolder * construct_copy(void * ptr) const \
{ return new(ptr) TYPE(*this) ; }
class FastBullet : public BulletHolder
{
private :
float accel ; //加速度がある
public :
BULLET_MAKE_CONSTRUCT_COPY_PP(FastBullet)
virtual void update()
{ std::cout << "fast" << std::endl ; }
virtual ~FastBullet() { }
} ;
class SlowBullet : public BulletHolder
{
private :
float r ; // 方向がある
public :
BULLET_MAKE_CONSTRUCT_COPY_PP(SlowBullet)
virtual void update()
{ std::cout << "slow" << std::endl ; }
virtual ~SlowBullet() { }
} ;
#undef BULLET_MAKE_CONSTRUCT_COPY_PP
class Bullet
{
private :
BulletHolder * ptr ;
static size_t const size = 16 ;
char buf[size] ; //BulletHolderを継承するすべてのクラスが収まるサイズ
private :
void * getbufptr()
{ return static_cast< void * >( &buf[0] ) ; }
void safe_destruct()
{
if ( ptr != 0 )
ptr->~BulletHolder() ;
}
public :
Bullet() : ptr(0) { }
template < typename T >
Bullet ( T const & x )
{
BOOST_STATIC_ASSERT(( boost::is_base_of<BulletHolder, T>::value )) ;
BOOST_STATIC_ASSERT(( sizeof(T) <= size )) ;
ptr = new( getbufptr() ) T(x) ;
}
template < typename T >
Bullet & operator = ( T const & x )
{
BOOST_STATIC_ASSERT(( boost::is_base_of<BulletHolder, T>::value )) ;
BOOST_STATIC_ASSERT(( sizeof(T) <= size )) ;
safe_destruct() ;
ptr = new( getbufptr() ) T(x) ;
}
Bullet( Bullet const & b )
{
if (b.get() != 0)
ptr = b.get()->construct_copy(getbufptr()) ;
}
Bullet & operator = ( Bullet const & b )
{
if ( this != &b && b.get() != 0)
{
safe_destruct() ;
ptr = b.get()->construct_copy(getbufptr()) ;
}
return *this ;
}
BulletHolder * get() const
{ return ptr ; }
BulletHolder * operator *()
{ return get() ; }
BulletHolder * operator ->()
{ return get() ; }
~Bullet()
{ safe_destruct() ; }
} ;
int main()
{
std::vector<Bullet> v(0) ;
v.push_back(FastBullet()) ;
v.push_back(FastBullet()) ;
v.push_back(SlowBullet()) ;
v.push_back(FastBullet()) ;
v.push_back(SlowBullet()) ;
for (std::vector<Bullet>::iterator iter = v.begin() ;
iter != v.end() ; ++iter )
{
(*iter)->update() ;
}
}
Bulletクラスに、すべての弾クラスが収まるサイズのバッファを用意し、そこにPlacement newでオブジェクトを構築している。
コピーコンストラクタの実装だけが、どうしてもスマートに行かない。
ここまで実装してはたと気づいた。
現実の弾幕STGでは、弾が別の弾を参照することがありえる(たとえばあるランダムに飛ぶ弾を基準に動く弾とか) すると、参照カウンタが必要だ。この場合、Bulletクラスで参照カウンタを実装して、intrusive_ptrにでも渡せばいいのではないだろうか。
そもそも、いくら多数といっても、弾幕STGの弾数は、せいぜい数百程度で、数千も行くことはないだろう。たとえshared_ptrに頼り、さらにSTLのlistを使ったとしても、本当にパフォーマンス上問題があるのか。私のCore2Quadはそんなに遅いのか?
そして、今頃気づいたのだが、上記のコードのコピーコンストラクタをスマートに書くためには、マルチメソッドが必要なのではないだろうか。そうすれば、BulletHolderクラスは、Bulletクラスのことを考えずに、へんなマクロによるおまじないも必要ないのではないだろうか。
と思ったが、マルチメソッドがあったとしても、結局BulletクラスがBulletHolderを継承しているすべてのクラスを網羅する必要があり、あまり違いはないか。
これアラインメントとか大丈夫?
ReplyDeleteそれもありましたね。
ReplyDeleteたとえば、これがWindows向けにVC9の32bitコードでコンパイルされたとして、BulletHolderの最初のメンバが8バイトのdoubleとか__int64形だったとしたら、アラインメント合いませんね。
やっぱり無駄に最適化したつもりになるのはやめたほうが賢明のようです。
せっかく面白いコードを考え付いたと思ったのに。
ReplyDelete現実は複雑です。
boost::alignmentなんたらでいけると思います
ReplyDeleteというよりこれはpolyだと思います
http://d.hatena.ne.jp/mb2sync/20070928