ページ 1 / 1
dynamic_castについて
Posted: 2012年2月21日(火) 21:53
by SUE
なんだかんだでこれが初質問になります。SUEです。
C++には基底クラスのポインタ→派生クラスのポインタ、というダウンキャストを行うdynamic_castがありますよね。これについてなのですが、ネットで情報を集めたところ、どうやら基本的に推奨されない機能だということが判りました。確かに、実行時判定する分遅いですし、そのために無駄な容量を食うことりもなります。
じゃあ自分で安全確保の機能を作って(容量は諦めるとして)static_castをすればいいんじゃね、と思ったのですが、さらに調べると、ダウンキャストそれ自体が推奨されないものだと判りました。
ダウンキャストをする事にも一応意味はあるし、普通にvirtualつき関数を作るのとも棲み分けはできると思うのですが、何故なのでしょう。それなりのオブジェクト指向の思想的理由があるような気がするのですが、思いあたる節がありません。どなたかお教えください。
オフトピック
これが解決すればシステムが完成するッ・・・!
Re: dynamic_castについて
Posted: 2012年2月22日(水) 03:10
by DVDM
>>SUE さん
遅いと言っても特に気にするほどではないと思います。
スーパークラスからサブクラスへダウンキャストすると、
スーパークラスが知らないメンバーがサブクラスにはある可能性があるため危険になりますし、
仰っている通り、static_cast を使ってキャストすることは可能ではありますが多態性が失われてしまうように思います。
本当に安全が確保され、かつ意味があるのであれば使えばいいと思いますが、
やはり色々とバグの原因になったりもすると思いますので推奨はされないのではないでしょうか。
Re: dynamic_castについて
Posted: 2012年2月24日(金) 01:32
by GRAM
そもそもどういった場合にダウンキャストが必要になるのでしょうか?
「オブジェクト指向の思想的理由」かどうかはわかりませんが、
通常はアップキャストしてしまったら、基底クラスのポインタもしくは参照からは
どの派生クラスからアップキャストされたものかわからないような使い方をするものなのでは?
(アップキャストしたら抽象化されてしまうから、ふつうそのキャストは非可逆になるはず。)
・・・しかしどうなんでしょう?オブジェクト指向を無視して単に使い方だけ考えれば、
確実にダウンキャストが安全でチェック機構が要らないような実装もありそうな気がします。
たとえば
①抽基底クラスのテンプレート引数に派生クラスを入れて、基底クラスのメンバ関数でダウンキャスト
(オブジェクト指向的にどうかはおいておいて)
一応実験してみました
► スポイラーを表示
コード:
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でポインタをたくさん使う代わりにポインタ一つだけを使えばいい)
Re: dynamic_castについて
Posted: 2012年2月24日(金) 01:53
by SUE
DVDMさん、ご意見ありがとうございます。で、いろいろ考えていたため若干放置っぽくなってしまいました。すみません。
GRAM さんが書きました:そもそもどういった場合にダウンキャストが必要になるのでしょうか?
ええとですね、継承でまとめたいクラスがあるけど、それぞれが独自にメンバをたくさん持ってたりする時、ですかね。いや、だったら最初から継承させるな、という感じですが。
結局悩んだ末、今はダウンキャストなしの設計にできました(しました)。よくよく考えると、確かにダウンキャストを使うってのはどこかが間違ってる証ですよね。
僕はGRAMさんみたいにテンプレートを活用したやり方は頭がついていけない(有限機械状態(゚д゚)?みたいな)のでやりませんが、大変参考になりました。しかしダウンキャストを例にとっても
色んな方法があるもんですね。改めてC++の奥の深さに絶望させられました。だからこそ勉強のしがいもあるのですが。