使用受保护的析构函数dynamic分配类数组

如果我有一个类定义类似

class A { protected: ~A(){ } }; 

那么我可以dynamic地分配个人以及对象的数组

 A* ptr1 = new A; A* ptr2 = new A[10]; 

但是,当我定义这个类的构造函数

 class A { public: A(){} protected: ~A(){ } }; 

那么我可以创build个人对象

 A* ptr = new A; 

但是当我尝试dynamic分配对象的数组

 A* ptr = new A[10]; 

编译器(gcc-5.1和Visual Studio 2015)开始抱怨A ::〜A()是不可访问的。

任何人都可以解释一下:

1-为什么在构造函数被定义和未定义的行为的差异。

2-当构造函数被定义为什么我被允许创build单个对象而不是对象数组。

按照C ++ 11,§5.3.4¶17,拒绝一个带有受保护的析构函数的new数组是正确的:

如果new-expression创build一个对象或类types的对象数组,则为分配函数,释放函数(12.5)和构造函数(12.1)完成访问和歧义控制。 如果新的expression式创build了一个类types的对象数组,访问和歧义控制是为析构函数(12.4)完成的。

(强调增加;在C ++ 03中使用的语句几乎完全相同,第5.3.4节); C ++ 14移动了一些东西,但似乎并没有改变问题的核心 – 请参阅@Baum mit Augen的答案)

这是因为new[]只有在所有元素都被构造的情况下才能成功,并且要避免在一个构造函数调用失败的情况下泄漏对象。 因此,如果它设法构build – 比如说前9个对象,而第10个对象失败并且例外,那么在传播exception之前必须破坏前9个对象。

注意,如果构造函数被声明为noexcept ,逻辑上这个限制是不需要的,但是在这方面标准似乎没有任何例外。


所以,这里gcc在第一个例子中在技术上是错误的,就标准而言,gcc也应该被拒绝,尽pipe我认为gcc做的是正确的(在实践中,没有办法A的默认构造函数可以抛出)。

事实certificate,gcc在这里是不正确的。 在N4141(C ++ 14)中,我们有:

如果new-expression创build了一个类types的对象数组,则可能调用析构函数(12.4)。

(5.3.4 / 19 [expr.new])和

如果可能调用的析构函数被删除,或者在调用的上下文中不可访问,那么该程序是不合格的。

(12.4 / 11 [class.dtor])。 所以这两个数组都应该被拒绝。 (Clang确实如此, 活着 。)

其原因是,正如其他人所提及的,以及我的旧的,不正确的答案,类types的元素的构build可能会失败,例外。 当发生这种情况时,必须调用所有完全构造的元素的析构函数,因此析构函数必须是可访问的。

当使用operator new (不带[] )分配单个元素时,此限制不适用,因为如果单个构造函数调用失败,则不能完全构造该类的实例。

我不是一个语言律师(非常熟悉这个标准),但是怀疑答案是和Baum mit Augen早些时候所给出的一样(被删除,所以只有那些有足够声誉的人才能看到)。

如果后续数组元素的构造失败并引发exception,那么已经构造的元素将需要被删除,需要访问析构函数。

但是,如果构造函数是noexcept ,则可以排除这个,并且不需要访问析构函数。 即使在这种情况下,gcc和clang都仍然抱怨,这可能是一个编译器错误 。 也就是说,编译器没有考虑到构造函数是noexcept 。 或者,编译器可能在标准范围内,在这种情况下,这听起来像是标准中缺陷