2016-07-03 2 views
3

Это никогда не происходило со мной, что с ++ имеет указатель ковариации и, следовательно, позволяет снимать себя в ногу, как это: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++ становятся умнее с каждым годом. Кто знает, что будет положение дел, лет через ...

Однако незначительные каламбур:

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

Нет. Это не первый элемент. это второй элемент. b[0] будет первым элементом. Кроме того, b не является массивом, это указатель. И это указатель на один элемент. Это не указатель на массив из двух элементов.

И вот откуда происходит неопределенное поведение.

Причина это не массив, потому что:

Base * b = children1; 

children1 распадается на Child1 *. Если это закончилось, вы могли бы сказать, что b будет указателем на двухэлементный массив.

Но это не то место, где все закончилось. Разложившийся указатель был отброшен до Base *. Вы можете неявно отнести указатель на подкласс к указателю на суперкласс. Но (свободно говоря сейчас) вы не можете наложить указатель на массив подклассов на массив суперклассов. Следовательно, b является, строго, указателем на один элемент, а b[1] становится неопределенным поведением.

+0

Я привык вызывать b [0] нулевой элемент. И фактически создание массива не обязательно. Я могу создать отдельный Child1 child1; База * b = & child1; * b = child2; и получить такое же неопределенное поведение, не так ли? – Amomum

+0

Нет, это было бы совершенно определенное поведение. –

+0

@Amomum: Конечно, только потому, что он хорошо определен, это не значит, что это хорошая идея. Это также не означает, что он делает то, что, по вашему мнению, делает. –

Смежные вопросы