Vectors + Classes = Confusing

This is a discussion on Vectors + Classes = Confusing within the C++ Programming forums, part of the General Programming Boards category; Yeah I forgot the increment....oops. The copy that calls release is the copy in the vector.
It is perfectly valid ...

Yeah I forgot the increment....oops. The copy that calls release is the copy in the vector.

It is perfectly valid in COM to do this:

IUnknown *ptr1;
IUnknown *ptr2;

ptr2=ptr1;

So the interface is copyable. It doesn't actually create another interface but it creates a pointer to an already valid interface. But you are right the problem is not the vector cleanup it is the cleanup performed by the person using the vector which is what I was pointing out. Using user types with vectors normally requires some type of cleanup on the class's part - more than just the standard vector memory cleanup.

I'm not sure that clear has to be called. My books do not specifically say anything about having to call clear for correct cleanup to occur. However if you were using the vector of classes approach and the cleanup was in the destructor code for that class, then calling clear() for that vector would in turn call the destructor for that class which would in turn clean up correctly. Without calling clear there you would have to use the delete [] mechanism.

But since a vector can be of any type I think its a very important topic to discuss. Don't assume that everything is cleaned up automatically. The memory required for the vector is, but the class must still cleanup its own memory if it has allocated any as well as any resources it may have used.

So far my code works quite well for this very thing. The only error I have is in the sound code which for some reason is doing what you are telling me. It is calling release() twice for the same object when it shouldn't be. But none of the other vectors do this - each IDirect3DTexture9 interface represents one surface. I admit that my method could be dangerous if you were to misuse the class, but as long as you follow the rules it works very well and it is very fast.

>> I'm not sure that clear has to be called.
It doesn't. If you have object instances in a container, the destructors for those objects will be called when clear() is called or when the container's destructor is called.

>> Without calling clear there you would have to use the delete [] mechanism.
I think you meant to say: If you have new allocated pointers within a container, you have to delete[] those pointers before calling clear() and before the containter's destructor is called.

The problem with using the above smart pointer with containers (or just in general) is that it is not copyable, which is why the copy constructor and assignment operator are disabled. If you did allow copying, then you would have two different instances of AutoReleaseIUnknown, each containing the same pointer, and each Releasing the same pointer. Since STL containers do a lot of copying, this would not be a good solution to avoid explicitly releasing the data inside the vector. A little bit of tweaking could make it quite useful, though.

So how do you avoid the double release? From what I see in my code each IDirect3DTexture9 interface will be released only once. The vectors and the data they contain is private to the class so its impossible to willy nilly re-copy the vectors or the data. The compiler will tell you that its impossible to access the data because its private. Perhaps if I created a static vector - but then all instances of the container must contain the same texture which is not what I want. I want multiple instances of the class with multiple uses for it.

The sequence ALWAYS exists inside of the game even if zero instances of it are playing. It is a resource that is out there as long as the game is running. When you need an explosion you add one and play it. At cleanup the vector of IDirect3DTexture9 interfaces are released. In this way I have several animation resources sitting out in memory just waiting to be used by the engine. For a class to use it you just simply add a user to the list and it tracks the current frame of animation that class is on. It then returns the correct texture to the class so the class can render it. All the container is, is simply a container of textures that can be used by any number of classes. The return function for the vectors would be something like this:

The FramePositions tracks the current frame for that user. For instance if it is FramePosition[0] that is user number 0. So when that class says gimme my next frame and it passes 0 to the class...the class will return the current frame for user 0. I realize that this could be heavily misused - like say if the user class released the IDirect3DTexture9 interface...it would be really really bad and the whole game would come crashing down. But as long as everyone plays by the rules and realizes who is responsible for releasing and who isn't...it works like a charm.

And its going to get really confusing when I have a vector of AnimationSequences and a class to handle those as well. So you can add animation sequences to the list, which have a list of animation frames, animations users, and animation frame times.

So the entire system is encapsulated by the CAnimSeqHandler class which has a vector of CAnimSeq objects which also has a vector of CAnimFrames objects, a vector of unsigned int FramePositions, and a vector of float FrameTimes. This allows me to create literally hundreds of animation sequences - yet not take nearly as much memory. Each animation frame only exists ONCE in the entire game. It is only loaded once, allocated once, and destroyed once. It took a long time to figure out how to make all this work.

And now that we have hijacked this thread.....I'd like to discuss this with you in another post or through PMs. It's extremely important to my game design and to efficient memory management. Holding 10000 instances of the same image in each class is crazy and that is what I'm trying to avoid. If you have 10 frames of animation....load only 10 in memory and then 'link' users to those frames.

I wasn't sure if vector insertion requires the = operator to be defined, so I left it private to prevent reassignment of a used smart-pointer (incidentally, with clever use of reference counting you can probably make reassignment work too).

I'm not using my own IUnknown interface. As a result as long as I do not release pTemp at the end of the Add function I'm ok. Once the data is in the vector it's impossible to gain access to it unless you are allowed to by the class.

>>Dunno. Sounds kinda ignorant to do that.
I was thinking of what you'd do if you wanted to create a generic reference counting class. For example, in the constructor you could use a ... and push several pointers onto a vector<void*>; create an enum to access the appropriate objects and overload the [] operator... Then when the reference count reaches 0, simply call delete on each element of the vector. Something along those lines. Of course, it would probably be better to just create a base RefCounter class to inherit from, or whatever.

I'm not sure that clear has to be called. My books do not specifically say anything about having to call clear for correct cleanup to occur. However if you were using the vector of classes approach and the cleanup was in the destructor code for that class, then calling clear() for that vector would in turn call the destructor for that class which would in turn clean up correctly. Without calling clear there you would have to use the delete [] mechanism.

Calling clear() on a member vector last in a destructor has absolutly no effect.

I guess you could use smart pointers with reference counting for the textures, but it's a matter of taste.

Originally Posted by Epo

I may toss the Vector::clear() method into the destructor, just for peace of mind.

Nope... most use an array and just reallocate when necessary (they allocate in chunks that are usually larger than the size of the vector so that for a while pushing onto the end of the vector does not require re-allocation). This is necessary because vectors must have their elements in a contiguous block of memory so that they can be compatible with C style arrays.