开发者

Why does C++ behave this way?

开发者 https://www.devze.com 2022-12-31 21:03 出处:网络
#include<stdio.h> class A { public: int a;}; class B: public A { int c; int d; }; int main() { A* pA = new B[10];
#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;
}

The second printf prints a junk value.

It should开发者_运维知识库 figure that it is pointing to an object of type B and increment by the sizof(B).

Why does that not happen?


No it shouldn't. The declared type of pA is A*, so it is incremented by sizeof(A), which leaves it pointing to the middle of the first B in the array.


The reason it's fragile is that you're side-stepping everything it does to try to keep you safe. Until you're sufficiently experienced to know why these problems are arising, and how to avoid them, you should:

  1. Forget that printf exists. Use std::cout instead.
  2. Forget that new exists. Use std::vector instead.

You should probably also read the C++ FAQ, and pay close attention to the part that says something to the effect that: "Even if an X is a Y, an array of X is not an array of Y."

Edit: As to why you're seeing the behavior you are, it's pretty simple: pointer arithmetic is defined in terms of the static type, not the dynamic type. That means it's based entirely on the type of pointee you defined for the pointer, NOT what it's pointing at. If you say it's pointing at an A, but then point it at a B, the arithmetic will still be done as if it was pointing at an A as you said it would.


It can only know that at runtime. Imagine it slightly changed

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

But that's not going to happen. C++ puts emphasize in speed, but this would basically make it into a language that ensures safety of operations. There is Java, C# and others that already solve this.

Kernel and device driver developers don't want a clever language runtime. They just want to have things run fast.

Have a look at Common undefined behavior in C++ question for all the things that will need to get "fixed" along. It won't be C++ anymore!


Pointer a points to an object that has static type A and dynamic type B. Pointer arithmetic in C++ works in terms of static type. So, from the point of view of pointer arithmetic, a points to an object of type A.


the objects have no record of what or how large they are, it's just allocated memory. The only way the compiler knows how to treat the object at the pointer memory, is by looking at the pointer type. So based on pointer A*, it will only assume an object of sizeof(A).


For reasons of compatibility with C arrays degrade to pointers. The type of B[10] may degrade to B*, and inheritance means that the assignment of a pointer to B to a variable of type A* is valid.

You then increment the value of this pointer, which adds the size of A to its address.

However, your assumption that incrementing a pointer is a valid operation if the pointer is not pointing to an array of elements of the type of the pointer is not correct.

If you try and combine the parts of C++ which are there so it behaves like C with the more strongly typed OO features, the looser typing in the C parts defeats the stronger typing in the C++ parts. It's best to keep them separate, or at least document the expected behaviours.


You're incremeneting the variable a, which is a locally declared pointer of A objects. That's the same as saying a=a+sizeof(A).

Since sizeof(B)>sizeof(A), you end up pointing into the middle of the first object. When C++ then adds the appropriate offset, it'll end up reading the c field of the first B object. That happens to be unitialized memory, containing "junk".

0

精彩评论

暂无评论...
验证码 换一张
取 消