Is it correct to do the following :
MyClass mc1(1, 1);
MyClass* pmc1 = &mc1;
delete pmc1;
I saw it in a code example. I thought we can call delete on pointers to object that is allocated on the heap but this pointer points to an object on the stack, isn't it ?
Could someone explain it.
No. You should never call delete on a pointer to the object that hasn't been created using new. It is undefined behavior.
C++ Standard n3337 § 5.3.5/2 Delete
(...)the value of the operand of delete may be a null pointer value, a
pointer to a non-array object created by a previous new-expression, or
a pointer to a subobject (1.8) representing a base class of such an
object (Clause 10). If not, the behavior is undefined.
Related
I'm wondering if the following is undefined?
int main()
{
struct Doggy { int a; ~Doggy() {} };
Doggy* p = new Doggy[100];
p[50].~Doggy();
p[50].a = 3; // Is this not allowed? The destructor was called on an
// object occupying that area of memory.
// Can I access it safely?
if (p[50].a == 3);
}
I guess this is generally good to know, but the reason I'm specifically wanting to know is that I have a data structure consisting of an array, where the buckets can be nullable by setting a value, kind of like buckets in a hash table array. And when the bucket is emptied the destructor is called, but then checking and setting the null state after the destructor is called I'm wondering if it's illegal.
To elaborate a little, say I have an array of objects and each object can be made to represent null in each bucket, such as:
struct Handle
{
int value = 0; // Zero is null value
~Handle(){}
};
int main()
{
Handle* p = new Handle[100];
// Remove object 50
p[50].~Handle();
p[50].value = 0; // Set to null
if (p[50].value == 0) ; // Then it's null, can I count on this?
// Is this defined? I'm accessing memory that was occupied by
// object that was destroyed.
}
Yes it'll be UB:
[class.dtor/19]
Once a destructor is invoked for an object, the object's lifetime ends; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended ([basic.life]).
[Example 2: If the destructor for an object with automatic storage duration is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined. — end example]
p[50].~Handle(); and later delete[] p; will make it call the destructor for an object whose lifetime has ended.
For p[50].value = 0; after the lifetime of the object has ended, this applies:
[basic.life/6]
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of type void* is well-defined. Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. The program has undefined behavior if:
6.2 - the pointer is used to access a non-static data member or call a non-static member function of the object
Yes, it's mostly. Handle::value is just an offset to a pointer of type Handle, so it's just going to work wherever you point it to, even if the containing object isn't currently constructed. If you were to use anything with virtual keyword, this would end up broken though.
p[50].~Handle(); this however is a different beast. You should never invoke destructors manually unless you have also explicitly invoked the constructor with placement new. Still not illegal, but dangerous.
delete[] p; (omitted in your example!) is where you end up with double-destruction, at which point you are well beyond UB, straight up in the "it's broken" domain.
I tried to deallocate same pointer twice and it failed, but if I follow the same steps with not making it NULL the code runs fine.
#include <iostream>
struct MyClass {
MyClass() {std::cout << "Allocated and Constructed" << std::endl ;}
};
int main () {
// allocates and constructs five objects:
MyClass * p1 = new MyClass[5];
delete[] p1;
delete[] p1; // The code will succeed if I comment this line of code
p1=NULL;
delete[] p1;
delete[] p1;
return 0;
}
I see a good answer to the question What happens when you deallocate a pointer twice or more in C++? but what makes it run if I make it NULL, shouldn't be the same behaviour to follow for both the cases?
You need to deallocate only what you allocate. You allocate five instances of MyClass with new[]. So that's what you need to deallocate.
You're not deallocating any pointers. Pointers don't need to be deallocated unless you dynamically allocated them, and your code doesn't dynamically allocate any pointers.
When you make the pointer nullptr (or NULL), it doesn't point to anything. So calling delete on it has no effect. The C++ standard chose to make calling delete (or delete[]) on a null pointer do nothing.
delete[] p1; doesn't ususally change the actual value of p1. (Although the C++ standard states that it can set p1 to nullptr on doing this, no compiler I've worked with actually does this).
So the behaviour on a second call to delete[] p1; is undefined since your program no longer owns that memory.
Some programmers consider it good practice to set p1 = nullptr explicitly after a delete[] since then a subsequent delete is benign. But doing that can hide other memory management issues your program has (why would your program attempt to delete the same block of memory more than once?), so I'd advise against it.
From the standard, 5.3.5$2 Delete [expr.delete] (bold by me)
In the first alternative (delete object), the value of the operand of
delete may be a null pointer value, a pointer to a non-array object
created by a previous new-expression, or a pointer to a subobject
(1.8) representing a base class of such an object (Clause 10). If not,
the behavior is undefined. In the second alternative (delete array),
the value of the operand of delete may be a null pointer value or a
pointer value that resulted from a previous array
new-expression.81 If not, the behavior is undefined.
It explains why delete a pointer twice is UB, and delete a null pointer is well defined.
class base
{
virtual ~base();
};
class der1 :public base
{
int i;
}
class der2 :public base //I used double that der2 is bigger than der1
{
double d;
}
int main()
{
base* ptr = new der2;
ptr->~base(); //Destructor call just for the
//case that the der2 had a pointer member
der1* ptr2 = static_cast<der1*>(ptr);
*ptr2 = der1();
delete ptr;
return 0;
}
What would happen if you would execute the code shown above?
Would this produce a memory leak and if yes why?
Is there a possibility to use the taken memory for different types without releasing it first? (Please no answers like why should you need this, it's just interest)
Edit:
The ~base() does nothing because this is an example.
It's undefined behaviour since you assign to, and delete an object after its lifetime has ended, which is forbidden by C++11, [basic.life]/5:
after the lifetime of an object has ended and before the storage which the object occupied is
reused or released, any pointer that refers to the storage location where the object will be or was located
may be used but only in limited ways. The program has undefined behavior if:
the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a
delete-expression
the pointer is used to [...] call a non-static member function of the object
(plus a few other restrictions.) You try to do both of these things.
If you were to fix this, by creating a new object in the old memory rather than assigning to it as if there were already an object there:
der1* ptr2 = new (ptr) der1;
then it's still undefined behaviour, as stated in C++11, [basic.life]/7
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or
released, a new object is created at the storage location which the original object occupied, a pointer that
pointed to the original object [...] can
be used to manipulate the new object, if [...] the new object is of the same type as the original object
(plus a few other conditions). You use an pointer to the old object to manipulate (specifically, to delete) the new object of a different type.
I have been trying to understand storage reuse in C++. Imagine we have an object a with a non-trivial destructor whose storage is reused with a placement new-expression:
struct A {
~A() { std::cout << "~A()" << std::endl; }
};
struct B: A {};
A* a = new A; // lifetime of *a begins
A* b = new(a) B; // storage reuse, lifetime of *b begins
[basic.life/8] specifies:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if the original object is transparently replaceable (see below) by the new object.
Since in my example the lifetime of *a has not ended when we reuse the storage it occupies, we cannot apply that rule. So what rule describes the behavior in my case?
The applicable rule for this is laid out in §3.8 [basic.life]/p1 and 4:
The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
the storage which the object occupies is reused or released.
4 A program may end the lifetime of any object by reusing the storage
which the object occupies or by explicitly calling the destructor for
an object of a class type with a non-trivial destructor. For an object
of a class type with a non-trivial destructor, the program is not
required to call the destructor explicitly before the storage which
the object occupies is reused or released; however, if there is no
explicit call to the destructor or if a delete-expression (5.3.5) is
not used to release the storage, the destructor shall not be
implicitly called and any program that depends on the side effects
produced by the destructor has undefined behavior.
So A *b = new (a) B; reuses the storage of the A object created in the previous statement, which is well-defined behavior provided that sizeof(A) >= sizeof(B)*. That A object's lifetime has ended by virtue of its storage being reused. A's destructor is not called for that object, and if your program depends on the side effect produced by that destructor, it has undefined behavior.
The paragraph you cited, §3.8 [basic.life]/p7, governs when a pointer/reference to the original object can be reused. Since this code doesn't satisfy the criteria listed in that paragraph, you may only use a only in the limited ways permitted by §3.8 [basic.life]/p5-6, or undefined behavior results (example and footnote omitted):
5 Before the lifetime of an object has started but after the storage
which the object will occupy has been allocated or, after the lifetime
of an object has ended and before the storage which the object
occupied is reused or released, any pointer that refers to the storage
location where the object will be or was located may be used but only
in limited ways. For an object under construction or destruction, see
12.7. Otherwise, such a pointer refers to allocated storage (3.7.4.2), and using the pointer as if the pointer were of type void*, is
well-defined. Such a pointer may be dereferenced but the resulting
lvalue may only be used in limited ways, as described below. The
program has undefined behavior if:
the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a
delete-expression,
the pointer is used to access a non-static data member or call a non-static member function of the object, or
the pointer is implicitly converted (4.10) to a pointer to a base class type, or
the pointer is used as the operand of a static_cast (5.2.9) (except when the conversion is to void*, or to void* and
subsequently to char*, or unsigned char*), or
the pointer is used as the operand of a dynamic_cast (5.2.7).
6 Similarly, before the lifetime of an object has started but after
the storage which the object will occupy has been allocated or, after
the lifetime of an object has ended and before the storage which the
object occupied is reused or released, any glvalue that refers to the
original object may be used but only in limited ways. For an object
under construction or destruction, see 12.7. Otherwise, such a glvalue
refers to allocated storage (3.7.4.2), and using the properties of the
glvalue that do not depend on its value is well-defined. The program
has undefined behavior if:
an lvalue-to-rvalue conversion (4.1) is applied to such a glvalue,
the glvalue is used to access a non-static data member or call a non-static member function of the object, or
the glvalue is implicitly converted (4.10) to a reference to a base class type, or
the glvalue is used as the operand of a static_cast (5.2.9) except when the conversion is ultimately to cv char& or cv unsigned char&, or
the glvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid.
* To prevent UB from cases where sizeof(B) > sizeof(A), we can rewrite A *a = new A; as char c[sizeof(A) + sizeof(B)]; A* a = new (c) A;.
There are some potential problems with this:
If B is larger than A, it will overwrite bytes not allocated - which is undefined behaviour.
Destructor of A is not called for a (or b - your code doesn't show whether you delete a or delete b or neither). This is very important if either for A or B destructor is doing something like reference counting, locks, memory deallocation (including std:: containers such as std::vector or std::string), etc.
If a is not used again after you create b, you still need to call the A destructor to make sure it's lifetime is over - see the example in the third bulled after the section you quoted. So if your purpose was to avoid the "expensive" destructor call, then your code is failing to abide by the rules given in section 3.8/7 of the standard.
You are also breaching the bullet of:
The original object was a most derived object (1.8) of type T and the new object is a most derived object of type T.
as A is not the most derived type.
In summary, "broken". Even in cases where it does work (e.g. changing to A* a = new B;), it should be discouraged, as it can lead to subtle and difficult bugs.
As an addendum, in order to do this correctly you may call the destructor explicitly.
Note: the located memory is of size B to accommodate the potential size between A and B.
Note 2: with your implementation of class A this will not work. ~A() must be made virtual!!
A *b = new B; //Lifetime of b is starting. It is important that we use `new B` rather than `new A` so as to get the correct size.
b->~B(); //lifetime of b has ended. The memory still remain allocated however.
A *a = new (a) A; //lifetime of a is starting
a->~A(); // lifetime of a has ended
// a is still allocated but in an undefined state
::operator delete(b); // release the memory allocated without calling the destructor. This is different from calling 'delete b'
I believe that calling operator delete on a base pointer should be safe. Please do correct me if this is not the case.
Alternatively, if you allocate the memory for a as a char buffer, you can then use placement new to construct A and B objects, and safely call delete[] to deallocate the buffer (since char has a trivial destructor):
char* buf = new char[sizeof(B)];
A *a = new (a) A;
a->~();
A *b = new (a) B;
b->~B();
delete[] buf;
string * str=new string;
delete str;
when I delete 'str' which points to an object, do two destructors get called - one for the pointer itself, and one for the object it points to?
What would the pointer's destructor do?
delete just causes the object that the given pointer is pointing at to be destroyed (in this case, the string object. The pointer itself, denoted by str, has automatic storage duration and will be destroyed when it goes out of scope like any other local variable.
Note, however, that non-class types do not have destructors. So even when you use delete with non-class types, no destructor is called, but when the pointer goes out of scope, it gets destroyed as normally happens with any other automatic variable (means the pointer just reaches the end of its lifetime, though the memory pointed to by the pointer is not deallocated until you use delete to explicitly deallocate it.).
The pointer it self doesn't been destructed by the delete statement. but as any scope variable it's been destroyed when the scope ends.
Example:
void Function()
{
string * str=new string;
delete str; // <-- here the string is destructed
} // <-- here the pointer is "destructed", which is mean it's memory freed from the stuck but no actual destruction function is called..
The concept of destructor is applicable only to objects (i.e. entities defined with class or struct), not to plain types, like a pointer is. A pointer lives just like a int variable does.
when I delete 'str' which points to an object, do two destructors get called - one for the pointer itself, and one for the object it points to?
No. delete takes a pointer argument. It destroys the object that's pointed to (using its destructor, if it has one, and doing nothing otherwise), and deallocates the memory that's pointed to. You must previously have used new to allocate the memory and create the object there.
The pointer itself is not affected; but it no longer points to a valid object, so you mustn't do anything with it. This is sometimes known as a "dangling pointer".
What would the pointer's destructor do?
Nothing. Only class types have destructors.
The destructor for a raw pointer, like your example of std::string*, is trivial (just like the destructors for other primitive types: int, double, etc.)
Smart pointer classes have non-trivial destructors that do things like free resources, adjust reference counts, etc.
I like the simplification you get from the notion that every type has a destructor. That way you don't have a mental glitch with a template that explicitly destroys a stored value, even if the type of that stored value is an int or a pointer:
template <class T> struct wrapper {
unsigned char data[sizeof(T)];
wrapper(T t) { ptr = new (data) T; }
~wrapper() { (T*)&data->~T(); } // ignore possible alignment problem
wrapper<int> i(3);
However, the destructors for ints and pointers are utterly trivial: they don't do anything, and there is no place you can go to see the definition of the destructor, because the definition doesn't exist. So it's also reasonable to say that they don't have destructors.
Either way, when a pointer goes out of scope it simply disappears; no special code runs.