根据C ++ 03标准(5.3.4 / 7):
当direct-new-declarator中的表达式的值为零时, 调用分配函数来分配没有的数组 元件。
通过我的阅读,这意味着此代码是合法的并具有指定的效果:
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
A() : a_(++aa_) {};
int a_;
static int aa_;
};
int A::aa_ = 0;
int main()
{
A* a = new A[0];
// cout << "A" << a->a_ << endl; // <-- this would be undefined behavior
}
当我在调试器下运行此代码时,我发现永远不会调用A
的构造函数。 new
不会抛出,并返回一个非空的,显然有效的指针。但是,a->a_
处的值是未初始化的内存。
问题:
a
实际指向什么?答案 0 :(得分:5)
在上面的代码中,实际指向了什么?
指向零元素数组。
“分配没有元素的数组是什么意思?”
这意味着我们得到一个有效的数组,但大小为零。我们无法访问它中的值,因为没有,但我们现在每个零大小的数组指向不同的位置,我们甚至可以将迭代器设置为过去的&a[0]
。所以我们可以像使用其他所有数组一样使用它。
分配具有零元素的数组有什么实际用途?
每次拨打n = 0
时,只会让您免于检查new
。请注意,零大小的静态数组是非法的,因为静态数组具有来自常量表达式的静态大小。此外,允许您使用一对迭代器调用任何标准或自定义算法。
答案 1 :(得分:4)
您的a
点(有效地)为零连续元素,并且已构造零元素。表达式a->a_
与a[0].a_
相同,它超出范围,因此未定义行为。
此外,语句“a->a_
的值是未初始化的内存”似乎没有意义:值是值,内存是内存 - 值不能 记忆。既然我们已经确定你必须永远不要取消引用a
,那么要求a
指向的变量的任何值都是没有意义的:a
只是没有指向任何有用的地方。
说delete[] a;
释放已分配的内存并销毁所有零对象仍然完全有效(并且期望?!)。
(注意,也可以使用参数malloc
调用C的0
函数,但是除了它free
之外,不保证它的返回值。在C ++中,{{实际上,每次都需要返回不同的,非null 值(需要重用已传递给::operator new[]
的值)。
答案 2 :(得分:3)
归结为:new whatever[0]
导致返回有效地址,不同于任何其他当前分配的对象,但不能取消引用。
它的典型用途是使用代码分配不同大小的数组,因此有时候你可能有一个元素数量正数的数组,但有时候数组的元素数为零。即使您不能取消引用它,使用“正常”(有效)指针而不是(例如)空指针仍然可以简化相当多的代码。
仅举一个例子,考虑存储数组起始地址的典型情况,以及超出数组末尾的地址(并且具有从一个到另一个遍历的循环)。使用空指针,你不能像那样进行算术运算,所以任何处理一个过去结束指针的东西都需要一个空指针的特殊外壳。对于无法取消引用的有效非空指针,您仍然可以形成起始地址和结束地址,而无需任何特殊情况。而不是只为非空指针执行循环的特殊情况代码,您编写正常循环,当数组为空时执行零迭代。