Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic allocation of class array with protected destructor

If I have a class defined like

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

then I can dynamically allocate the individual as well as array of objects like

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

However when I define the constructor for this class

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

then I can create individual objects with

A* ptr = new A; 

but when I try to dynamically allocate the array of object with

A* ptr = new A[10]; 

compiler(gcc-5.1 and Visual Studio 2015) starts complaining that A::~A() is inaccessible.

Can anyone explain about:-

1- Why is the difference in behavior with constructor being defined and not defined.

2- When the constructor is defined why I am allowed to create individual object and not array of object.

like image 838
Dinesh Maurya Avatar asked Jan 21 '17 12:01

Dinesh Maurya


2 Answers

As it turns out, gcc is not correct here. In N4141 (C++14), we have:

If the new-expression creates an array of objects of class type, the destructor is potentially invoked (12.4).

(5.3.4/19 [expr.new]) and

A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.

(12.4/11 [class.dtor]). So both array cases should be rejected. (Clang does get that right, live.)

The reason for that is, as mentioned by others and by my old, incorrect answer, that the construction of elements of class type can potentially fail with an exception. When that happens, the destructor of all fully constructed elements must be invoked, and thus the destructor must be accessible.

That limitation does not apply when allocating a single element with operator new (without the []), because there can be no fully constructed instance of the class if the single constructor call fails.

like image 23
Baum mit Augen Avatar answered Oct 14 '22 20:10

Baum mit Augen


Rejecting an array-new with a protected destructor is correct, as per C++11, §5.3.4 ¶17:

If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function (12.5), and the constructor (12.1). If the new expression creates an array of objects of class type, access and ambiguity control are done for the destructor (12.4).

(emphasis added; almost exactly the same wording is used in C++03, §5.3.4 ¶16; C++14 moves some stuff around, but that doesn't seem to change the core of the issue - see @Baum mit Augen's answer)

That comes from the fact that new[] succeeds only if only all the elements have been constructed, and wants to avoid leaking objects in case one of the costructor calls fails; thus, if it manages to construct - say - the first 9 objects but the 10th fails with an exception, it has to destruct the first 9 before propagating the exception.

Notice that this restriction logically wouldn't be required if the constructor was declared as noexcept, but still the standard doesn't seem to have any exception in this regard.


So, here gcc is technically wrong in the first case, which, as far as the standard is concerned, should be rejected as well, although I'd argue that "morally" gcc does the right thing (as in practice there's no way that the default constructor of A can ever throw).

like image 156
Matteo Italia Avatar answered Oct 14 '22 21:10

Matteo Italia