为什么这是一个错误:
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::vector
,std::array
,等通常比C风格的数组更受欢迎,但我有一个“现实世界”我需要使用上述类型的用例。
答案 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 *h
,h[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 ++标准中new
和new[]
是特别分开的,因为出于性能和效率的原因,它们可以是不同的分配器;分配数组与单个对象具有不同的分配器实现优化的使用模式。
语法可能有所不同,因为标准化时可用的编译时类型内省技术并不像现在这样可靠。
要制作合理的代码,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];