2017-11-16 6 views
1

ををstatic_cast:私はTextViewの継承の順序を変更した場合、私はitv->setText();呼び出しでセグメンテーション違反があるでしょうC++複数のインタフェースを継承し、次のコードでは

Live

#include <iostream> 
#include <thread> 
#include <mutex> 
#include <functional> 

struct IView { 
    virtual void setOnClick() = 0; 
}; 
struct ITextView : IView { 
    virtual void setText() = 0; 
}; 
struct IButton : ITextView { 
    virtual void setRadius() = 0; 
}; 

struct View : IView { 
    int i = 1; 
    virtual void setOnClick() override { 
     std::cout << "setting OnClick! i: " << i << std::endl; 
    }; 
}; 

/// Works as is 
/// But if make "TextView : View, ITextView" - have segfault on the run 
struct TextView : ITextView, View { 
    int j = 2; 
    virtual void setText() override { 
     std::cout << "setting text! i: " << i << " j: " << j << std::endl; 
    }; 

    // forward IView 
    virtual void setOnClick() override { 
     View::setOnClick(); 
    } 
}; 


int main() { 
    TextView tv; 

    void* ptr = &tv; // I need to pass raw pointer, and then restore "interface" from it 

    ITextView* itv = static_cast<ITextView*>(ptr); // I don't need safety checks here 
    itv->setOnClick(); 
    itv->setText(); 

    return 0; 
} 

なぜ重要ですか?ここでstatic_castを使用できますか、またはここにUBがありますか?私が理解しているように、仮想継承でのみ必要なのはdynamic_castで、これは私が言うことができるように、その場合ではありません。

+1

試す 'ITextView * itv = &tv; void * ptr = itv;' –

答えて

5

明示的にTextView*からvoid*まで、次にvoid*からITextView*まで明示的に。これらの変換は、void*からのキャスト時にポインタ調整を行わないため、実際にTextViewITextViewサブオブジェクトではありません)を指すタイプITextView*のポインタで終わります。未定義の動作が続きます。

TextView tv; 

void* ptr = static_cast<ITextView*>(&tv); // Adjust, then convert to void* 

ITextView* itv = static_cast<ITextView*>(ptr); 
4

異なるクラスのスライスへのポインタは異なります。

TextView <- ITextView <- ... <- IView 
      \- View <- IView 

があります:あなたはボイド*経由tranportとき

だから、あなたはあなたの基本クラス階層にはIViewの二つの "インスタンス" を持つ

void * ptr = static_cast<ITextView *>(&tv); 
... 
ITextView* itv = static_cast<ITextView*>(ptr) 

注意する必要がそれを「ダイヤモンド」に変換する仮想継承:wikipedia

+0

OPの例では、このUBは?私にとってセグメンテーションはありません。その意味は – Fureeish

+2

@Fureeish - はい、それはUBです。それ以上のUBになるためにクラッシュする必要はありません。 – StoryTeller

+0

静的なキャストが各行で異なる何かをしているという価値はありません。 – StoryTeller

0

ポイントは、あなたがすることによって、異なるタイプの通過にstatic_castをやっているということです。

ソリューションはvoid*の両方の「側面」にまったく同じタイプを使用して常にの世話をすることですvoid*実際の型をコンパイラに隠す。これは安全である

TextView tv; 
ITextView* itv = static_cast<ITextView*>(&tv); 

、明示的に親の型に変換されます。

は、私は何を意味していることです。ここで

TextView tv; 
void* ptr = &tv; 
ITextView* itv = static_cast<ITextView*>(ptr); 

コンパイラはptrの実際のタイプを知っているとITextView*としてそれを考慮して、それができないオブジェクトの正しいサブパートへのポインタを調整することはありませんが(見えるかもしれませんということに気付い価値がありますITextViewにはメンバー変数がないために動作します)。さて、これは明らかであるあなたがABにアドレスを持っている場合と

--------------------- 
| VTable A | Type A | 
--------------------- 

--------------------- 
| VTable B | Type B | 
--------------------- 

----------------------------------------- 
| VTable AB | Type A | Type B | Type AB | 
----------------------------------------- 

が、これは

class A { /* members */ }; 
class B { /* members */ }; 
class AB : public A, public B { /* members */ }; 

ためのメモリレイアウトであると仮定します。これは、継承がフードの下に実装される方法に関連して、考えますコンパイラがのタイプBの部分(タイプAの後に存在する)を指すことができないことをコンパイラに知らせずに、B*にキャストします。これは最初にABであったことを知ることによってのみ知ることができました。

関連する問題