tags:

views:

204

answers:

7

Hello,

This is a newbe question but I have alwasy doubts with pointers in C++. This is the situation.

I have a class A which as a collection (a vector actually) of pointers of class B. This same class A has another collection of pointers to class C. Finally the objects of class B have also a collection to pointers to class C which point to the same instances the class A points to.

My question is, if I delete a member of class-C-type pointer in class B, what happens to the pointer in class A that points to the deleted instance of class C? How this situation has to be treated?

Thanks a lot in advance!

Julen.

+6  A: 

My understanding:

A
 - vector<B*>
 - vector<C*>

B
 - vector<C*>

From within B you are deleting all of the C*.


You need to delete each memory address separately, but only at most once.

Any pointer to a deleted address is still holding the same address, it just produces undefined behavior if you use it.

So be sure not to re-delete the B* from A that you already deleted, and be sure not to use them after they are deleted.


You may want to re-consider your hierarchy/design though.

Or consider using boost::shared_ptr if you need to store things in this way. Actually anytime you're storing a collection of pointers you should probably be using boost::shared_ptr. If you are using boost::shared_ptr you don't delete and you don't need to worry about invalidating the other pointers.

Brian R. Bondy
+1 Esp. with the re-consider part. Shared ownership is seldom a good solution. The shared_ptr might look seductive, but it is IMHO for special scenarios. When you find yourself needing shared ownership of objects, reevaluate your architecture, to see if shared ownership is really what is needed.
daramarak
better is a collection of unique_ptrs but only the newest version of VC++ and GCC have unique_ptrs.
caspin
+1 for shared_ptr
BlueRaja - Danny Pflughoeft
`boost::shared_ptr` and probably `boost::weak_ptr` used in combination, depending on the actual semantics (should pointers in `A` be 'invalidated' when they are removed from `B`?)
David Rodríguez - dribeas
First of all, thanks everyone for your contributions. Yes, I was expecting the answer to be like this. The thing is, in terms of design, how can I improve this hierarchy. Class B is the real owner of Class C, but at the same time I like to keep track of all instances of Class C in Class A. (Class B knows about some Class C instances, not all). The thing is that I am not "allowed" to use boost and I'd like to learn to design good programs.
Julen
@Julen: It's hard to say without knowing exactly what you're trying to do. But please post a new question as that way you'll have other people to help.
Brian R. Bondy
A: 

It's always a good idea to set old pointers to NULL when whatever they've been pointing on has been deleted - unless you're destroying the list of pointers next.

illuzive
Although that is good advice, setting `B`'s pointer values to NULL won't change the pointers in `A` in @Julen's case.
Brian R. Bondy
@Illusive: no, it's *not* a good idea! That's bad practice both in C and C++. Doing this you'll just replace bugs that cause segfaults by bugs that do memory leaks (and usually *much* harder to find). Also in this case of pointers in object A, they won't be changed if you delete pointed objects in another class. Using RAII is a better idea.
kriss
@kriss: yes, RAII is the best solution, but saying that setting raw pointers to NULL after deletion is bad practice is laughable at best. If you _have_ to deal with raw pointers, it is an absolute necessity that you set deleted or unused pointers to NULL.
I guess I didn't explain what I meant in a good enough manner. I didn't mean that every pointer always should be set to NULL as soon as the object/variable the pointer is pointing to is deleted. However, when dealing with lists of pointers, or pointers that might point to allocated objects again in the future - it's necessary to set the pointer to NULL in order to make sure that old objects are deleted before allocation of new ones occur. Also, old pointers may still point to an old value, or at least a "working" value. In that case it's much better to point at NULL and find the bug in time.
illuzive
And by necessary I mean that you can't check if the pointer points at an object or not later on. It's very easy to check if it's pointing at NULL though. I couldn't get that into my earlier comment due to the text limit.
illuzive
@pdusen: I'm not saying you should never do that. Some common data structures in algorithm books (lists, tree) uses NULL pointers as a marker for empty node (but any distinguished element could do). Setting pointers to NULL as an habit is clearly bad practise. It is a code smell of poorly designed object life-cycle, showing that some objects are not instanciated in constructors and deleted in destructors. An exemple of consequence : I had to deal not long ago with a source code where about 50% of the executed code was checking uselessly that pointers where not NULL, including static buffers...
kriss
@pdusen: and I wonder where you show absolute necessity here... there is so many ways to deal with raw pointers to deleted objects: typically remove them from container, use some kind of reference counter, set them to some distinguished Empty element, use some kind of flag, etc. The most common, if dealing with deletion in destructor being doing nothing as pointers will just go ou of scope. *Absolute necessity*... I'm the laugher.
kriss
A: 

If you delete the instances to class C from class B, then the pointers in class A still point to those addresses... resulting in undefined behavior (most likely a seg fault). You must make sure that if you delete an instance in one place, that all other references to that instance are also updated.

In your case, you'd want to clear out A's collection to class C.

An alternative would be to use smart pointers... you could have multiple references to the same instance, and the instance is only deleted when no more references exist.

Polaris878
+4  A: 

Consider using a suitable smart pointer to hold your raw pointers in the containers, instead of using straight up raw pointers, this will save you the trouble of figuring out how you are to manually delete them. These usually also support custom deallocation functions and functors, in case you need that. You could probably look into shared_ptr or weak_ptr, depending on what you need. You might also find unique_ptr to be useful, it all depends on what your requirements are.

Jacob
I think you mean: 'shared_ptr or weak_ptr' haha?
Polaris878
+1, but how would you choose between shared_ptr or shared_ptr?
Pete Kirkham
I'm going to try to answer the question implied by a combination of Polaris878 and Pete. The difference between a shared_ptr and a weak_ptr. The shared_ptr claims ownership, and so when the last shared_ptr gives up ownership the object is destroyed. The weak_ptr claims no ownership, and so is not counted against ownership claims. Usually, having a weak_ptr to something implies that there is a shared_ptr to that same object somewhere too. We once had a topological structure that that had the topological connections maintained by weak_ptrs and the list of nodes maintained with smart_ptrs.
McBeth
Oh man, talk about a brain fart there, I'll correct that.
Jacob
A: 

If you delete the object pointed to by a pointer inside class C, actually nothing will happen to pointers to the same objects in other classes. That is they will hold the same value as before and pointed to some desallocated memory.

Several solutions :

  • write code to remove pointers from other user classes when deleting from C class
  • use smart pointers thus the objects pointed to will only be removed when no pointer will point to them.

I do not much like the second solution, even if it is very common, as it is sign of a poor control of your objects life cycle. You should probably reconsider your design.

kriss
A: 

If you wish to use pointer and dynamic allocation, you need to define who owns what.

In your case, deleting the Cs from the Bs is dangerous, because after the Bs have been destroyed, they have taken the Cs with them even though A still have a reference to them.

In your situation, I would find it better to do the following:

  • destroying a C does not do anything
  • destroying a B does not do anything
  • destroying a A destroys its collection of B's and C's

It means that A is the owner of the memory and manages the life cycle of the objects, whereas Bs only have references to the Cs.

Also, I would advise using RAII (Resources Acquisition Is Initialization) so that the cleanup is done automatically for you:

class A
{
public:
private:
  std::vector< std::unique_ptr<B> > bs;
  std::vector< std::unique_ptr<C> > cs;
};

// B and C are unchanged

The use of unique_ptr has 3 advantages:

  • it clearly states who the owner of the memory is (in this case the instance of A)
  • it prevents unintended copying of the A instance which would wreak havoc in your desing
  • it simplifies the handling of memory, it's automatic, don't worry about it
Matthieu M.
A: 
daramarak