2016-07-03 6 views
3

それは私へのポインタの共分散を持つ++そのCを発生したことがないので、あなたは、このような足で自分自身を撮影することができます:それは未定義の動作C++ポインタの共分散

struct Base 
{ 
    Base() : a(5) {} 
    int a;  
}; 

struct Child1 : public Base 
{ 
    Child1() : b(7) {} 
    int b; 
    int bar() { return b;} 
}; 

struct Child2 : public Base 
{ 
    Child2(): c(8) {} 
    int c; 
}; 

int main() 
{ 
    Child1 children1[2]; 

    Base * b = children1; 

    Child2 child2; 

    b[1] = child2; // <------- now the first element of Child1 array was assigned a value of type Child2 

    std::cout << children1[0].bar() << children1[1].bar(); // prints 57 
} 

ですか?それを防ぐ方法はありますか、少なくともコンパイラからの警告はありますか?

答えて

2

はい、これは未定義の動作です。

ここで、典型的なC++コンパイラでは、ここでは診断に役立つ何かを識別することはできません。しかし、C++のコンパイラは、1年ごとに賢くなります。最初の要素ではありません

b[1] = child2; // <------- now the first element of Child1 array was assigned... 

番号:誰が、しかし...今から数年、事務の状態がどうなるかのマイナーな屁理屈を

を知っています。 2番目の要素です。 b[0]が最初の要素になります。さらに、bは配列ではなく、ポインタです。そしてそれは単一の要素へのポインタです。これは2要素配列へのポインタではありません。

これは未定義の動作の原因です。

Base * b = children1; 

children1Child1 *に減衰:ので、それは配列ではありません

理由があります。それが出来事が終わったところなら、bは2要素配列へのポインタになると言うことができます。

しかし、それが終わった場所ではありません。減衰したポインタはBase *にキャストされていました。サブクラスへのポインタをスーパークラスへのポインタに暗黙的にキャストできます。しかし、(今のところ大まかに言って)、スーパークラスの配列にサブクラスの配列へのポインタをキャストすることはできません。したがって、bは厳密には単一の要素へのポインタであり、b[1]は未定義の動作になります。

+0

私はゼロ要素b [0]を呼び出すのに慣れています。実際に配列を作ることは必要ではありません。私は単一のChild1 child1を生成することができます。ベース* b =&子1; * b = child2;同じ未定義の動作を取得することはできませんか? – Amomum

+0

いいえ、これは完全に定義された動作です。 –

+0

@Amomum:言うまでもなく、それが定義されているという理由だけでは、それは良い考えではありません。それがあなたが思っていることをしているという意味でもありません。 –