代码之家  ›  专栏  ›  技术社区  ›  Aakash Goel

为什么C++会这样做?

  •  4
  • Aakash Goel  · 技术社区  · 15 年前
    #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 回复  |  直到 15 年前
        1
  •  7
  •   Community CDub    8 年前

    它只能在运行时知道这一点。想象一下它有点变化

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

    但这不会发生。C++强调速度,但这基本上使它成为一种确保操作安全的语言。Java、C等已经解决了这个问题。

    内核和设备驱动程序开发人员不想要一个聪明的语言运行时。他们只想让事情进展得更快。

    看一看 Common undefined behavior in C++ 所有需要“修复”的问题。它不再是C++了!

        2
  •  19
  •   Stephen C. Steel    15 年前

    不,不应该。声明的类型 pA A* ,因此它的增量为 sizeof(A) ,使其指向数组中第一个b的中间。

        3
  •  11
  •   Jerry Coffin    15 年前

    它之所以脆弱,是因为你站在一边,尽一切努力保护你的安全。在您有足够的经验了解这些问题产生的原因以及如何避免这些问题之前,您应该:

    1. 忘了 printf 存在。使用 std::cout 相反。
    2. 忘了 new 存在。使用 std::vector 相反。

    你可能还应该阅读C++ FAQ,并密切关注某一部分的含义:“即使X是Y,X的数组也不是Y的数组。”

    编辑:至于您看到自己行为的原因,这非常简单:指针算术是根据静态类型而不是动态类型定义的。这意味着它完全基于您为指针定义的指针类型,而不是它所指向的类型。如果你说它指向一个A,然后再指向一个B,那么算术仍然会像你说的那样指向一个A。

        4
  •  3
  •   AnT stands with Russia    15 年前

    指针 a 指向具有 静态类型 A 动态类型 B . C++中的指针运算 静态类型 . 所以,从指针算法的角度来看, 指向类型为的对象 .

        5
  •  1
  •   catchmeifyoutry    15 年前

    对象没有记录它们的大小,只是分配了内存。编译器知道如何在指针内存中处理对象的唯一方法是查看指针类型。因此,基于指针a*,它将只假设一个sizeof(a)的对象。

        6
  •  1
  •   Pete Kirkham    15 年前

    由于与C数组的兼容性,降级为指针。类型 B[10] 可能退化为 B* 和继承意味着指针的赋值 B 到类型的变量 A* 是有效的。

    然后,您增加这个指针的值,这会增加 A 到它的地址。

    但是,如果指针未指向指针类型的元素数组,则假定递增指针是一个有效的操作,这是不正确的。

    如果您尝试将C++中的部分结合起来,使它的行为与C一样,具有更强类型的OO特性,则C部分中的较松散类型将在C++部分中破坏更强的类型。最好将它们分开,或者至少记录下预期的行为。

        7
  •  1
  •   Michael Madsen    15 年前

    你在增加变量 a ,它是对象的本地声明指针。这和说的一样 a=a+sizeof(A) .

    由于sizeof(b)>sizeof(a),您最终指向第一个对象的中间。当C++添加适当的偏移量时,它将结束读取 c 第一个b对象的字段。这恰好是包含“垃圾”的统一内存。