そもそもどういった場合にダウンキャストが必要になるのでしょうか?
「オブジェクト指向の思想的理由」かどうかはわかりませんが、
通常はアップキャストしてしまったら、基底クラスのポインタもしくは参照からは
どの派生クラスからアップキャストされたものかわからないような使い方をするものなのでは?
(アップキャストしたら抽象化されてしまうから、ふつうそのキャストは非可逆になるはず。)
・・・しかしどうなんでしょう?オブジェクト指向を無視して単に使い方だけ考えれば、
確実にダウンキャストが安全でチェック機構が要らないような実装もありそうな気がします。
たとえば
①抽基底クラスのテンプレート引数に派生クラスを入れて、基底クラスのメンバ関数でダウンキャスト
(オブジェクト指向的にどうかはおいておいて)
一応実験してみました
► スポイラーを表示
コード:
template< typename Type >
struct Base
{
public:
void DoSomething()
{
Type* p = static_cast<Type*>(this);
p->DoSomething();
}
};
struct A
: public Base<A>
{
public:
void DoSomething()
{
std::cout << "A:DoSomething" << std::endl;
}
};
struct Base2
{
public:
virtual void DoSomething() = 0;
};
struct B
: public Base2
{
public:
void DoSomething()
{
std::cout << "B:DoSomething" << std::endl;
}
};
int main(){
A a;
Base<A>* base = &a;
base->DoSomething();
cout << sizeof(A) << endl;
B b;
Base2* base2= &b;
base2->DoSomething();
cout << sizeof(B) << endl;
}
・・・まぁvirtualを使わない分サイズは減るのかな?w汎用性はすごく低そうですが・・・
その②
基底クラスのプロテクテッドなメンバに派生クラスからアクセスする。
こっちの方が幾分か実用的な気もします。(やはりオブジェクト指向的にどうかはおいておいて)
► スポイラーを表示
コード:
class Base
{
private:
std::vector<Base*> container;
protected:
virtual void DoSomething( Base* ){};
void Show()
{
std::cout << "Base::Show" << endl;
}
public:
void Execute()
{
for( auto it = container.begin(); it != container.end(); ++it ) (*it)->DoSomething(this);
}
void Add( Base* b)
{
container.push_back( b );
}
};
template< int N >
class ShowN:
public Base
{
void DoSomething( Base* b )
{
ShowN<N>* p = static_cast<ShowN*>(b);
for( int i = 0; i < N; ++i ) p->Show();
std::cout << "-------------------" << endl;
}
};
int main(){
Base b;
b.Add( new ShowN<2> );
b.Add( new ShowN<3> );
b.Execute();
}
※メモリーリークへの突込みはなしでw
実は自分の場合こっちの方は結構使いますが、若干罪の意識に駆られながら使ってたりします・・・
これ使うと有限状態機械の設計が超楽ちんなんですよね。(containerでポインタをたくさん使う代わりにポインタ一つだけを使えばいい)