使用受保护的析构函数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
。 或者,编译器可能在标准范围内,在这种情况下,这听起来像是标准中的缺陷 。