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

时间:2017-01-21 12:30:40

标签: c++ c++11 visual-c++ language-lawyer

如果我的课程定义为

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

然后我可以动态分配个人以及像

这样的对象数组
A* ptr1 = new A;
A* ptr2 = new A[10];

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

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

然后我可以使用

创建单个对象
A* ptr = new A;

但是当我尝试用

动态分配对象数组时
A* ptr = new A[10];

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

任何人都可以解释: -

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

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

3 个答案:

答案 0 :(得分:17)

使用受保护的析构函数拒绝数组 - new是正确的,根据C ++ 11,§5.3.4¶17:

  

如果new-expression创建一个对象或类类型的对象数组,则对分配函数,释放函数(12.5)和构造函数(12.1)进行访问和歧义控制。 如果新表达式创建了类类型的对象数组,则对析构函数(12.4)进行访问和歧义控制。

(重点补充;几乎完全相同的措辞用于C ++ 03,§5.3.4¶16; C ++ 14移动了一些东西,但这似乎没有改变问题的核心 - 见 @Baum mit Augen 的答案)

这是因为new[]仅在构造了所有元素时才成功,并且在其中一个costructor调用失败的情况下希望避免泄漏对象;因此,如果它设法构造 - 比如说 - 前9个对象但第10个对象因异常而失败,它必须在传播异常之前破坏前9个。

请注意,如果构造函数声明为noexcept,则逻辑上不需要此限制,但在这方面标准似乎没有任何异常。

所以,这里gcc在第一种情况下在技术上是错误的,就标准而言,也应该被拒绝,尽管我认为“道德”gcc做正确的事情(在实践中是这样的) A的默认构造函数无法抛弃。

答案 1 :(得分:10)

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

  

如果   new-expression创建一个类类型的对象数组,可能会调用析构函数(12.4)。

(5.3.4 / 19 [expr.new])和

  

一个   如果可能调用的析构函数被删除或无法从上下文访问,则程序格式错误   调用。

(12.4 / 11 [class.dtor])。 因此,两个阵列案例都应该被拒绝。(Clang确实做到了,live。)

原因是,正如其他人和我以前的错误答案所提到的那样,类类型元素的构造可能会因异常而失败。当发生这种情况时,必须调用所有完全构造元素的析构函数,因此必须可以访问析构函数。

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

答案 2 :(得分:3)

我不是语言律师(非常熟悉该标准),但怀疑答案与Baum mit Augen先前给出的答案一致(已删除,因此只有声誉充足的人才能看到它)。

如果后续数组元素的构造失败并抛出异常,则需要删除已构造的元素,需要访问析构函数。

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