ページ 11

static_castによるダウンキャスト(C++)

Posted: 2010年6月04日(金) 20:26
by kaiten
 久しぶりにプログラムを組んでいたら分からない点があったので質問させていただきます。

 子クラスのメンバにアクセスしない保証があるという前提の場合、型が親クラスの変数のポインタを子クラスのポインタにstatic_castでダウンキャストしてもいいのでしょうか?

 親クラスのprotectedなメンバに子クラスの静的メンバ関数からアクセスしたいのですがこれが不可能らしいので子クラスのポインタに親クラスの変数のポインタをstatic_castで子クラスのポインタにダウンキャストして入れています。
 今のところ動いてはいるのですが単にバグが顕在化していないだけだと後々困るので確認したいのです。

以下やりたい事のコード
class A
    {
    protected:
        int test;
    };

class B
    : public A
    {
    public:
        static void Func(A* a)
            {
            B* ptr=static_cast<B*>(a);
            ptr->test=0;
            }
    };

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月04日(金) 21:46
by やんち
static_cast を使うよりも、dynamic_cast の方が、安全で簡単かも。

class A
{
public:
virtual ~A()
{
}
protected:
int test;
};

class B
: public A
{
public:
virtual ~B()
{
}

static void Func(A* a)
{
B* p = 0;
p = dynamic_cast<B*>(a);
if (!p)
{
fprintf(stderr, "*** ダウンキャストに失敗。\n");
}
else
{
p->test = 0;
}
}
};

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月04日(金) 21:54
by kaiten
>>やんちさん
返信ありがとうございます。

 dynamic_castの方が安全なのはわかっているのですが、今回の場合親クラスの型の変数のポインタをキャストしているのでdynamic_castだとNULLが返ってくると思うのですがどうでしょう。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月04日(金) 22:12
by Justy
 A::SetTest(int value)を publicで作成するではダメなのでしょうか?


>static_castでダウンキャストしてもいいのでしょうか?
 B::Funcに与える引数が 100% Bか Bを継承したインスタンスのポインタ
であることを保証できるのなら。
画像

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月04日(金) 22:13
by たかぎ
dynamic_castを使うには、多相オブジェクトへのポインタか参照をオペランドにとらなければなりません。
今回の場合、クラスAには仮想関数がひとつもありませんので、dynamic_castを使うことはできません。

static_castでダウンキャストを行う場合には、正しくキャストできることを自分で保証しなければなりませんが、それができるのであれば一応問題はありません。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月05日(土) 02:48
by pie
AとBの関係が密であれば、
class Aの中でfriend class Bを宣言すれば
a->testにアクセスできるので無意味なダウンキャストは避けられるかな。

ところで、
「派生クラスのメンバ関数から、thisでない基本クラスオブジェクト(のポインタや参照)のprotectedなメンバにアクセスできない。
でも自クラスであれば、thisじゃなくてもprotectedやprivateメンバにアクセスできる。」
という言語仕様に今さらながら少し驚きました。
整合性がとれてない気がします。

つまり、kaitenさんの例では
「a->testにアクセスできてもよさそうだし、
それが許されないのならptr->testも許されないべき」
な気が俺はするんですが。

便乗質問になってしまいますが、
こんな仕様になってるのには何かわけがあるんでしょうか?

ちなみにJavaで似たようなコードを書いて試したところ、
B.Func内でa.testに直接アクセスしてもコンパイルエラーにはなりませんでした。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月05日(土) 03:15
by たかぎ
> こんな仕様になってるのには何かわけがあるんでしょうか?

こうしておかないと、Aから派生したCを作れば、Bの部分オブジェクトをCから破壊できることになります。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月05日(土) 04:44
by pie
なるほど。たしかに。

Javaの場合はprotectedの制限が少し緩くて
同一パッケージ内からは全然関係ないクラスからでもアクセスできてしまう以上、
もともとその危険性は覚悟の上でprotectedにしてるんでしょ、ってことですね。

然るに、kaitenさんの状況は本来あってはならない状況ってことですね。
でも絶対ないかっていうとありえなくもないような・・・?
そうなるようなクラス設計がそもそもおかしいのでしょうかね。
まあ滅多にない状況ではあると思うので、
もし自分が直面したらその時また考えることにします。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月05日(土) 20:36
by kaiten
Justyさん、たかぎさん、pieさん、返信ありがとうございます。

>>Justyさん
正道できちんとやるならA::SetTest(int value)を作るべきだとは思うのですが、確実に親クラスのポインタである保証がある場合はstatic_castでどうにかならないかと思った次第です。親クラスを子クラスの静的メンバ関数内で操作したかったのでpublicなアクセス関数を用意しようとなると変数の数だけ作らなければならなくなるので……
ところで、BではなくAのポインタなのですが、これはつまり不可ということですか?

>>たかぎさん
デストラクタを仮想関数にしていたのですがうっかり書き忘れていました。たしかにこれではdynamic_castは使えませんね。
この場合、
A a;
B* b=static_cast<B*>(&a);
が正しいキャストであるならばよいということでしょうか。Aのメンバにしかアクセスしない前提の場合これは正しいと言えるのか知らないのでよろしければ教えていただけないでしょうか。

>>pieさん
私もポインタから直接アクセスできるものだと思っていました。その結果コンパイルエラーになったのでかなり慌てました。
確かに、仕様上できないことを無理に行おうとするのは設計がおかしいと言わざるを得ませんね。仕事でも課題でもないのをいいことに出来るか試すような書き方をしているので人には見せられないようなものになってます。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月06日(日) 14:44
by Justy
>ところで、BではなくAのポインタなのですが、これはつまり不可ということですか
 実際のところこのサンプルの範囲内であれば動くことは動くと思いますが、
この方法は潜在的な問題を抱えることになり、幾つか別の要素が入ってきたときに
問題になるかもしれません。

 それを承知の上であれば可です。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月06日(日) 15:16
by たかぎ
> A a;
> B* b=static_cast<B*>(&a);
> が正しいキャストであるならばよいということでしょうか。

正しいはずがありませんね。
強引なキャストをするつもりなら、いっそのこと、

A a;
int* p = reinterpret_cast<int*>(&a);
*p = 0;

とでもすればどうですか?
このほうが、やっていることの危険性がよくわかります。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月06日(日) 15:50
by kaiten
>>Justyさん
Bには静的メンバ関数以外追加しないのでこのサンプルの範囲内で収まると思います。なにか問題が起きたらその際に設計事見直そうと思います。

>>たかぎさん
確かに強引で危険なキャストであることを明示するためにreinterpret_castを使った方が良さそうですね。


今回は危険な方法であることを肝に銘じてreinterpret_castにして終了します。
これにて解決とします。ありがとうございました。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月06日(日) 20:03
by たかぎ
> 今回は危険な方法であることを肝に銘じてreinterpret_castにして終了します。

reinterpret_castで[color=red>int*[/color]にするなど、凶悪さを強調しておきべきですよ。
念のため繰り返しておきます。

Re:static_castによるダウンキャスト(C++)

Posted: 2010年6月06日(日) 22:22
by kaiten
>>たかぎさん
私が想定していたより遥かに凶悪な行動のようなのでアクセス関数を設けることにします。
少なくとも楽をするためにとっていい方法ではなく、全力を尽くして避けるべき方法だと認識しておきます。
普通に組めば直面しない状況でも有りますし。



終了宣言だけして解決押し忘れてました。すみません。今度こそ解決とします。