为什么这是一个错误:

typedef int H[4];

H * h = new H;        // error: cannot convert 'int*' to 'int (*)[4]' in initialization



此外,为什么这不是错误:
H * h = new H[1];



为什么编译器认为new H返回int *,而new H[1]返回预期的H *

换句话说:为什么T * t = new T;对于常规类型T是正确的,但是当T是数组类型时为什么不正确?

通过new分配诸如此类的简单数组类型的规范方法是什么?

请注意,这是一个简化的示例,因此new int[4]不是可接受的解决方法-我需要使用前面typedef中的实际类型。

还要注意,我知道使用std::vectorstd::array等通常比C样式数组更可取,但是我有一个“真实世界”用例,需要使用上述类型。

最佳答案

new T的返回类型和值的C++规则是:

  • 如果T不是数组类型,则返回类型为T *,并且返回值是指向T类型的动态分配对象的指针。
  • 如果TU类型的数组,则返回类型是U *,返回值是指向U类型动态分配的数组的第一个元素(其类型是T)的指针。

  • 因此,由于您的Hint的数组,因此new H的返回类型是int *,而不是H *

    根据相同的规则,new H[1]返回H *,但是请注意,您已经在技术上分配了int的二维数组,大小为1 x 4。

    在通用代码中解决此问题的最佳方法确实是使用auto:
    auto h = new H;
    

    或者,如果您希望突出显示指针事实:
    auto *h = new H;
    

    至于规则中看似不一致的基本原理:在C++中,数组指针是非常“危险的”,因为它们的行为非常出乎意料(即,您必须非常小心以免产生不良影响)。让我们看一下这段代码:
    typedef int H[4];
    H *h = obtain_pointer_to_H_somehow();
    h[2] = h[1] + 6;
    

    乍一看(甚至可能第二眼),上面的代码似乎将6添加到数组中的第二个int并将其存储在第三个int中。但这不是它的作用。

    就像int *p一样,p[1]int(位于距sizeof(int)偏移的p字节地址处),因此,对于H *h来说,h[1]H,位于距4 * sizeof(int)偏移地址h字节处。因此,代码被解释为:接收h中的地址,向其中添加4 * sizeof(int)字节,然后添加6,然后将结果地址存储在8 * sizeof(int)h偏移的位置。当然,这会失败,因为h[2]衰减为右值。

    好的,您可以这样修复它:
    *h[2] = *h[1] + 6;
    

    现在更糟。 []的绑定(bind)比*紧密,因此它将到达int之后的第5个h对象(请注意那里只有4个!),加6,然后将其写入int之后的第9个h中。写入随机存储器FTW。

    要实际执行代码可能打算执行的操作,必须将其拼写如下:
    (*h)[2] = (*h)[1] + 6;
    

    鉴于上述情况,并且由于通常对动态分配的数组执行的操作是访问其元素,因此new T[]返回T *更为有意义。

    关于c++ - `new`指向数组的指针的首选方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37203859/

    10-11 22:10
    查看更多