Delete [] after casting unsigned char * for const unsigned char *

Roughly speaking, I have a class that contains an array of const unsigned char. The objects of this class are created by the special factory function, which also takes care of building the array (on the heap). When an object is created in a factory, it is assigned a pointer to an array. The array will not be copied, the object will use this pointer. Upon destruction, it will free the part of the memory occupied by the array.

class Foo { private: const unsigned char* array; size_t size; public: Foo(const unsigned char* array, size_t size) : array(array), size(size) {} ~Foo() {delete [] array;} }; Foo* factoryFunction(const void* data, size_t size) { unsigned char* array = new unsigned char[size]; memcpy(array, data, size); return new Foo(array, size); } 

Now I wonder if there are any side effects, because new [] returns an unsigned char * , but delete [] is called for const unsigned char * . However, I am not getting any segmentation errors.

+8
c ++
source share
3 answers

This is good, and the non-normative text in the standard offers the same thing:

[C++11: 5.3.5/2]: If the operand is of class type, the operand is converted to a pointer type by calling the above conversion function, and the converted operand is used instead of the original operand for the rest of this section. In the first alternative (delete an object), the value of the delete operand can be the value of a null pointer, a pointer to an object without an array created by the previous new expression, or a pointer to a subobject (1.8) representing the base class of such an object (Section 10). If not, the behavior is undefined. In the second alternative (delete array), the value of the delete operand may be the value of the null pointer or the value of the pointer that was the result of the previous new expression. If not, the behavior is undefined. [Note: this means that the syntax of the delete expression must match the type of the object highlighted by new , and not the syntax of the new expression. -end note] [Note: a pointer to a const type can be an operand of a delete expression; there is no need to discard the constant (5.2.11) of the pointer expression before it is used as the operand of the delete expression. -end note]


There is a possible contradiction arising from the following places:

[C++11: 5.3.5/3]: In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type should be the base class of the dynamic type of the object to be deleted and the static type should have virtual destructor or undefined behavior. In the second option (delete array), if the dynamic type of the object to be deleted is different from its static type, the behavior is undefined.

Although the intent of this passage seems to me only to handle polymorphism, combined with the following passage, it can be interpreted as meaning that, strictly speaking, your code does indeed cause undefined behavior:

[C++11: 3.9.3/1]: [..] Qualified or cv-unqualified versions of a type are various types; [..]

However, I am sure that this can be considered as a defect in the wording of the standard; It seems to me that this is clear.


Similarly, this rule may even suggest that the program will not compile:

[C++11: 12.5.4]: [..] If the delete expression begins with the unary operator :: , the name of the release function is displayed in the global scope. Otherwise, if the expression-expression is used to free an object of the class whose static type has a virtual destructor, the release function is the one selected at the definition point of the virtual destructor of dynamic types (12.4). Otherwise, if the delete expression is used to free an object of class T or its array, the static and dynamic types of the object must be identical , and the name of the delete function is used in the scope of T. If this search does not find the name, the name is looked up in the global scope. If the search result is ambiguous or unavailable, or if the search selects the release function, the program is poorly formed.

But again, this is not the intention of the rule, which eliminates polymorphism in the absence of a virtual destructor and genuine ambiguities in several class declarations.


To summarize, your best bet when interpreting the wording of these rules is still on the non-normative, but very clear note that we started with.

+5
source share

There should not be any problems, since "const" -ness on an "unsigned char" (or any data type) makes it read-only.

Using "[]" in delete indicates that the array should be deleted.

To understand what happens behind "delete" vs "delete []", read this .

+1
source share

First of all, you should avoid using such arrays in C ++, instead you should use std::vector<unsigned char> .

about the const keyword. It simply tells the compiler and the one who reads the code that this member / variable / parameter should not be changed.

With pointers there, the position of the const keyword is important:

( EDIT : found Greyson's answer where I borrowed this part)
The const keyword denotes the part to the left of it as a constant (if it is at the beginning, it matches the type to the right of it, so const unsigned char and unsigned char const are equal)

To mark the pointer as const, you do this (the content is still changing):

 unsigned char * const aConstantPointerToAMutableContent; 

To mark the content as const, you will do the following:

 const unsigned char * aPointerToAConstantContentA; unsigned char const * aPointerToAConstantContentB; 

To mark as a constant:

 const unsigned char * const aConstantPointerToAConstantContent; 

This is a hint for the compiler and user to make it clear what is done or not done with the data. if the parameter is const unsigned char * , then the user will know that this content will not be changed if it is passed.

because the const keyword is only a label, if something can be changed, it does not affect the size of the size itself, so for delete it should not have an effect. (see answers and comments. Ease of race in orbit , as well as " Is const_cast safe? " may be for interrest).

But what I don't like about your code is that the constructor is publicly available. I could call it directly, but since the const unsigned char* array parameter is const unsigned char* array , I would not expect the class to change the contents or delete it when destroyed. (I would not expect direct object creation to behave differently using factory.)

So, I would make the factory method as the static method of this class and make the constructor protected .

+1
source share

All Articles