質問

私はC++のスライスの問題について読んで、いくつかの例を試しました(私はJavaの背景から来ています)。残念ながら、私はいくつかの行動を理解していません。現在、私はこの例に固執しています(Efficent C++第3版からの代替例)。誰かが私にそれを理解するのを助けることができますか?

親の私の単純なクラス:

class Parent
{
public:

    Parent(int type) { _type = type; }

    virtual std::string getName() { return "Parent"; }

    int getType() { return _type; }

private:

    int _type;
};

子供の私の単純なクラス:

class Child : public Parent
{

public:

    Child(void) : Parent(2) {};

    virtual std::string getName() { return "Child"; }
    std::string extraString() { return "Child extra string"; }
};

メイン:

void printNames(Parent p)
{
    std::cout << "Name: " << p.getName() << std::endl;

    if (p.getType() == 2)
    {
        Child & c = static_cast<Child&>(p);
        std::cout << "Extra: " << c.extraString() << std::endl;
        std::cout << "Name after cast: " << c.getName() << std::endl;
    }
}

int main()
{
    Parent p(1);
    Child c;

    printNames(p);
    printNames(c);
}

私が得る実行をAfte:

名前:親

名前:親

エクストラ:子の余分な文字列

キャスト後の名前:親

それは「スライス」の原因であるため、私は、最初の2行を理解しています。しかし、なぜ静的キャストを介して子を親にキャストできるのか理解できません。この本には、スライスした後、すべての専門情報がスライスされると書かれています。だから私は推測する、私はキャストできません pc, 、私は情報を持っていないので(関数printNamesの最初に、追加情報なしで新しい親オブジェクトが作成されます)。

さらに、なぜ私は"キャスト後の名前"を取得しているのですか:"キャスト後の名前"ではなく、キャストが成功した場合は"親":子供"?

役に立ちましたか?

解決

あなたの結果は不運の並外れた脳卒中です。これが私が得る結果です:

Name: Parent
Name: Parent
Extra: Child extra string
bash: line 8:  6391 Segmentation fault      (core dumped) ./a.out
.

これはあなたのコードで何が起こっているのか:

cprintNamesに渡すと、変換が発生します。特に、パスは値によるものであるため、暗黙的に宣言されているParentのコピーコンストラクタを呼び出し、そのコードがこのようになります。

Parent(Parent const& other) : _type{other._type} {}
.

それでも、_type c変数をコピーし、他にはにコピーします。現在、Parent型の new オブジェクトがあります(静的タイプと動的タイプの両方がParentです)、cは実際にはprintNamesにまったく渡されません。

関数内では、pChild&に強制的に変換します。 pは単にChildではなく、C ++では、C ++が診断されないため、C ++はあなたに診断されません(これは実際には恥が正しくないことがわかります)。< / P>

今、私たちは未定義の行動の国にあり、今すべてが起こることが許されます。実際には、Child::extraStringthisに(暗黙的または明示的に)アクセスされないため、その関数への呼び出しは成功するだけです。呼び出しは不正なオブジェクトで行われますが、オブジェクトは触れられないため、その機能はありません(まだ違法です)。

次の呼び出しは、Virtual Callで、一般的にChild::getNameにアクセスする必要があります(ほとんどの実装では、仮想メソッドテーブルポインタにアクセスします)。そしてやはりコードがUBであるため、何でも起こり得る。あなたは「ラッキー」で、コードは親クラスの仮想メソッドテーブルポインタをつかみました。私のコンパイラで、そのアクセスは明らかに失敗しました。

他のヒント

そのコードは恐ろしいです。何が起こっているのか:

  • printNames(c) スライス c, 、コピー構築ローカル p から Parent 呼び出し元のオブジェクトに埋め込まれたオブジェク c オブジェクト、その後、設定 p'sへのポインタ Parent 仮想ディスパッチテーブル。

  • のデータメンバーなので、 Parent からコピーされた c, 、タイプの p は2であり、 if 分岐が入力されます

  • Child & c = static_cast<Child&>(p); 効果的にコンパイラに「私を信頼してください(私はプログラマーです)、私はそれを知っています」と伝えます p 実際には Child 私が参照したいオブジェクト"ですが、それは露骨な嘘です p 実際には Parent からコピーされたオブジェクト Child c

    • 有効であるかどうかわからない場合は、コンパイラにこれを行うように依頼しないようにすることは、プログラマとしてあなたに責任があります
  • c.extraString() それは知っているので、コンパイラによって静的に(コンパイル時に)発見されます c である。 Child (またはさらに派生した型ですが、 c.extraString そうではありません virtual したがって、静的に解決できます;それは 未定義の動作 これを行うには Parent オブジェクトですが、おそらく extraString データにアクセスしようとしません。 Child オブジェクトは持っているでしょう、それは表向きはあなたのために「ok」を実行しています

  • c.getName()virtual, 、コンパイラはオブジェクトの仮想ディスパッチテーブルを使用します-オブジェクトは実際には Parent これは動的に(実行時に)解決されます。 Parent::getName 関数と関連する出力を生成します

    • ただし、仮想ディスパッチの実装は実装定義であり、未定義の動作は、すべてのc++実装で、またはすべての最適化レベルで、すべてのコンパイラオプ
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top