Почему C ++ ведет себя так? - PullRequest
4 голосов
/ 19 мая 2010
#include<stdio.h>

class A { public: int a;};

class B: public A {
    int c; 
    int d;
};

int main() {

    A* pA = new B[10];
    B* pB = new B[10];

    printf("\n%d", pA->a);
    pA++;
    printf("\n%d", pA->a);  // prints junk value

    printf("\n\n%d", pB->a);
    pB++;
    printf("\n%d", pB->a);
    return 0;
}

Второй printf печатает значение барахла.

Это должно означать, что оно указывает на объект типа B и увеличивается на sizof(B).

Почему этого не происходит?

Ответы [ 7 ]

19 голосов
/ 19 мая 2010

Нет, не должно. Объявленный тип pA равен A*, поэтому он увеличивается на sizeof(A), что указывает на середину первого B в массиве.

11 голосов
/ 19 мая 2010

Причина, по которой он хрупок, заключается в том, что вы обходите стороной все, что он делает, чтобы защитить вас. Пока вы не наберетесь достаточного опыта, чтобы понять, почему возникают эти проблемы и как их избежать, вам следует:

  1. Забудьте, что printf существует. Вместо этого используйте std::cout.
  2. Забудьте, что new существует. Вместо этого используйте std::vector.

Вероятно, вам также следует прочитать FAQ по C ++ и обратить пристальное внимание на часть, в которой говорится что-то вроде: «Даже если X - это Y, массив X не является массивом Y».

Редактировать: Относительно того, почему вы видите поведение, которое вы видите, все довольно просто: арифметика указателей определяется в терминах статического типа, а не динамического типа. Это означает, что он полностью основан на типе указателя, который вы определили для указателя, а не на то, на что он указывает. Если вы говорите, что он указывает на A, но затем указывает на B, арифметика все равно будет выполняться так, как если бы она указывала на A, как вы сказали.

7 голосов
/ 19 мая 2010

Он может знать это только во время выполнения. Представьте, что это немного изменилось

A* a;
if(runtimevalue)
  a = new A[10];
else
  a = new B[10];

Но этого не произойдет. C ++ делает акцент на скорости, но это в основном сделало бы его языком, обеспечивающим безопасность операций. Есть Java, C # и другие, которые уже решают эту проблему.

Разработчики ядра и драйверов устройств не хотят умного языка исполнения. Они просто хотят, чтобы все шло быстро.

Посмотрите на Распространенный неопределенный вопрос в C ++ для всех вещей, которые нужно будет исправить Это больше не будет C ++!

3 голосов
/ 19 мая 2010

Указатель a указывает на объект, имеющий статический тип A и динамический тип B. Арифметика указателей в C ++ работает в терминах статического типа . Таким образом, с точки зрения арифметики указателей, a указывает на объект типа A.

1 голос
/ 19 мая 2010

По соображениям совместимости с массивами C ухудшаются до указателей. Тип B[10] может ухудшиться до B*, а наследование означает, что присвоение указателя на B переменной типа A* допустимо.

Затем вы увеличиваете значение этого указателя, что добавляет к его адресу размер A.

Однако ваше предположение о том, что увеличение указателя является допустимой операцией, если указатель не указывает на массив элементов типа указателя, неверно.

Если вы попытаетесь объединить существующие части C ++, чтобы он вел себя как C с более строго типизированными функциями OO, более слабая типизация в частях C побеждает более сильную типизацию в частях C ++. Лучше держать их отдельно или, по крайней мере, документировать ожидаемое поведение.

1 голос
/ 19 мая 2010

объекты не имеют записи о том, что или как они велики, это просто выделенная память.Единственный способ, которым компилятор знает, как обрабатывать объект в памяти указателя, - это смотреть на тип указателя.Таким образом, основываясь на указателе A *, он будет принимать только объект размера (A).

1 голос
/ 19 мая 2010

Вы увеличиваете переменную a, которая является локально объявленным указателем на объекты. Это то же самое, что сказать a=a+sizeof(A).

Поскольку sizeof (B)> sizeof (A), вы в конечном итоге указываете на середину первого объекта. Когда C ++ затем добавит соответствующее смещение, в конечном итоге будет прочитано поле c первого B-объекта. Это происходит из памяти, содержащей «мусор».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...