Suppose there's a vector of Item
s
vector<Item*> items; //{item1, item2, item3}
Then, in other part of the code,
items[1]->suicide();
where the suicide
function is:
void Item::suicide()
{
delete this;
}
What is items
vector size and how it's arrangement now?
It is okay to do this?
Edit (may I ask an additional question?): If the desired arrangement of the output is {item1, item3}
, size is 2
, and no dangling pointer, how to do it in a self-destructing way (from the item2
itself)?
Edit 2 : Thanks for all the answers! Awesome. So I finally decided and found the way to do it from outside of the object because it was a bad practice and unnecessarily complicated
The vector has no idea what you're doing elsewhere in the code, so it'll keep a dangling pointer to the original
Item
.After suiciding the item, you should adjust the vector manually to no longer keep that dangling pointer.
The pointer will become invalid, that's all. You should be careful to not to
delete
it again.vector<Item*>
will NOT delete elements on its own.What is items vector size and how it's arrangement now? The same. The function call does not change the vector contents nor size at all. It just frees the memory the pointer is pointing to.
Is it okay to do this? More precisely: Is it legal C++? Yes. Is it good style programming? No. Let me elaborate on the latter:
There should be a separation of concerns: Who's responsible for memory management? The container or the user of the class
Item
or the classItem
itself?Typically the container or user should do that, because he knows what's going on.
What's the way to do that? Memory management in modern and safe C++ code is mostly done using smart pointers like
std::shared_ptr
andstd::unique_ptr
and containers likestd::vector
andstd::map
.If the class
Item
is a value type (that means you can simply copy it and it has no polymorphic behavior in terms of virtual functions), then just usestd::vector<Item>
instead for your code. Destructors will be called automatically as soon as an element is removed from the container. The container does it for you.If the class
Item
has polymorphic behavior and can be used as a base class, then usestd::vector<std::unique_ptr<Item>>
orstd::vector<std::shrared_ptr<Item>>
instead and prefer thestd::unique_ptr
solution, because it adds less overhead. As soon as you stop referring to an object, it will be deleted automatically by the destructor of the smart pointer you are using. You (almost) don't need to worry about memory leaks anymore.The only way you can produce memory leaks is that you have objects that contain
std::shared_ptrs
that refer to each other in cyclic way. Usingstd::unique_ptrs
can prevent this kind of trouble. Another way out arestd::weak_ptrs
.Bottom line: Don't provide a function
suicide()
. Instead put the responsibility solely into the hands of the calling code. Use standard library containers and smart pointers to manage your memory.Edit: Concerning the question in your edit. Just write
This will work for all types:
std::vector
of values or pointers. You can find a good documentation ofstd::vector
and the C++ Standard library here.Your
suicide
function does not to anything with theItems
vector, let alone it knows anything about it. So from the vector's point of view: nothing changes when you call the function and it's ok to do that.The suicide member doesn't change the vector. So the vector contains an element which is an invalid pointer and formally you can't do much with an invalid pointer, even copying or comparing it is undefined behavior. So anything which access it, included vector resizing, is an UB.
While any access if formally UB, there is a good chance that your implementation doesn't behave strangely as long as you don't dereference the pointer -- the rationale for making any access UB is machines where loading an invalid pointer in a register can trap and while x86 is part of them, I don't know of widespread OS working in this mode.
Wow, It seems that you make a typing error. It should be
vector<Item *> Items;
As to your question:Besides, the vector will manage its memory automatically, when you push some elems into it while the capacity is not enough, it will reallocate a larger space, BUT, when you pop some elems or erase some elems, it will never give the redundant memory to the system.
The code of
Items[1]->sucide()
just return the memory held or pointed by the pointer Items[1] to the system, it will do nothing on the pointer itself, Items[1] still holds the same value, but point an unsafe area.Unexpectedly, you have made a Design Pattern, suppose you want to design a class and you ONLY allow allocate any object of it on the Heap, you may write the following code:
Then ,the class can not have any instance that is alloacated on the stack, because the destructor is private, and the compiler must arrange the calling of destructor when the object walk out its scope. Therefor, you must allocate them on the heap,
MustOnHeap* p = new MustOnHeap;
and then destroy it explicitly : p->suicide();