指向数组的`new`指针的首选方法

时间:2016-05-13 07:30:23

标签: c++

为什么这是一个错误:

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风格的数组更受欢迎,但我有一个“现实世界”我需要使用上述类型的用例。

4 个答案:

答案 0 :(得分:15)

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

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

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

按照同样的规则,new H[1]会返回H *,但请注意您已经技术分配了int s的二维数组,大小为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;

首先(甚至可能是第二次)一瞥,上面的代码似乎在数组中的第二个int中加上6,并将其存储在第三个int中。但那它的作用。

int *p一样,p[1]int(地址sizeof(int)字节偏离p),因此H *hh[1]H,地址4 * sizeof(int)字节偏离h。因此,代码被解释为:获取h中的地址,向其添加4 * sizeof(int)个字节,然后添加6,然后将结果地址存储在8 * sizeof(int)的偏移h处。当然,这会失败,因为h[2]会衰减为右值。

好的,你可以这样解决它:

*h[2] = *h[1] + 6;

现在更糟糕了。 []*更紧密,所以这会在int之后到达第5个h对象(注意那里只有4个!),添加6,然后写在int之后进入第9 h。写入随机存储器FTW。

要实际执行代码可能的目的,它必须拼写为:

(*h)[2] = (*h)[1] + 6;

鉴于上述情况,并且由于您通常使用动态分配的数组来访问其元素,因此new T[]返回T *更有意义。

答案 1 :(得分:4)

主要问题

  

[什么是] new指向数组

的首选方法

约束:

  

另请注意,我知道使用std :: vector,std :: array等通常比C风格的数组更受欢迎,但我有一个真实的世界"我需要使用上述类型的用例。

答案:

表示非数组的情况:

#include <memory>

auto h = std::make_unique<H>();

// h is now a std::unique_ptr<H> which behaves to all intents and purposes
// like an H* but will safely release resources when it goes out of
// scope
// it is pointing to a default-constructed H

// access the underlying object like this:
h.get(); // yields H*
*h;      // yields H&

对于数组大小写:

#include <memory>

auto h = std::make_unique<H[]>(4);

// h is now a std::unique_ptr<H[]> which behaves to all intents and purposes
// like an H* but will safely destruct and deallocate the array 
// when it goes out of scope
// it is pointing to an array of 4 default-constructed Hs

// access the underlying object like this:
h[1];     // yields H& - a reference to the 2nd H
h.get();  //yields H* - as if &h[0]

答案 2 :(得分:2)

在C ++标准中newnew[]是特别分开的,因为出于性能和效率的原因,它们可以是不同的分配器;分配数组与单个对象具有不同的分配器实现优化的使用模式。

语法可能有所不同,因为标准化时可用的编译时类型内省技术并不像现在这样可靠。

要制作合理的代码,IMO的首选方式是:

struct H { int values[4]; }

H * h = new H;

这样,您的H类型逻辑“包含”您的四个int值的数组 - 但内存中的结构应该仍然兼容(assert(sizeof(H) == 4 * sizeof(int)));并且您可以使用固定大小数组的对象样式分配。更多... C ++ - ey。

答案 3 :(得分:1)

如果您基本上手动执行typedef替换,您可以看到原因:

html

缩减为:

H * h = new H;

,而

int[4]* h = new int[4];

缩减为:

H * h = new H[1];

由于数组指针衰减规则,这是合法的。

如果您想要实际的解决方法,并且您知道H的类型,则可以执行以下操作:

(int*)[4] h = new int[1][4];