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 ...

  1. #16
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    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.
    Last edited by VirtualAce; 12-15-2004 at 11:13 AM.

  2. #17
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,673
    >> 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.

    gg

  3. #18
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    Quote Originally Posted by Bubba
    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.
    This is fine. You are managing the ownership of the object yourself. You know when doing it that way that you must Release ptr1 or ptr2 but not both.

    One common technique to handle ownership is to use a smart pointer that releases in the destructor like you mentioned. For example:
    Code:
    class AutoReleaseIUnknown // NOT COMPLETE
    {
    public:
        AutoReleaseIUnknown(IUnknown* ptr) : ptr_(ptr) { }
        ~AutoReleaseIUnknown() { ptr_->Release(); }
        IUnknown* get() const { return ptr_; }
        IUnknown* operator->() const { return ptr_; }
    private:
        AutoReleaseIUnknown(const AutoReleaseIUnknown&);
        void operator=(const AutoReleaseIUnknown&);
        IUnknown* ptr_;
    };
    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.

  4. #19
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    Quote Originally Posted by Thantos
    in C++ struct and class only have one difference. struct defaults to public class defaults to private. Other then that there is no difference between them.
    Almost. A struct can be initialized via the old C style way (i.e. MyStruct foo = {member1, member2, member3}) whereas a class can't.

  5. #20
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    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.

    Animation sequences
    .
    .
    Textures
    .
    .-> User list

    So an animation is this:

    Sequence
    ..Frames data
    ..Vector data
    ..Timer data
    |
    |
    |
    User connects to this class and access frames through the sequence class
    When the sequence is done...it destroys itself.

    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:

    IDirect3DTexture9 *GetFrame(unsigned int _classID)
    {
    return (Textures[FramePositions[_classID]]);
    }

    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.
    Last edited by VirtualAce; 12-15-2004 at 12:29 PM.

  6. #21
    & the hat of GPL slaying Thantos's Avatar
    Join Date
    Sep 2001
    Posts
    5,681
    Quote Originally Posted by master5001
    Almost. A struct can be initialized via the old C style way (i.e. MyStruct foo = {member1, member2, member3}) whereas a class can't.
    Really? Hmmm wonder why this works:
    Code:
    #include <iostream>
    using namespace std;
    
    class Foo {
      public:
        int x;
        int y;
    };
    
    struct Bar {
      int x;
      int y;
    };
    
    ostream& operator<< (ostream& o, const Foo& f)
    {
      return o<<f.x<<' '<<f.y;
    }
    
    ostream& operator<< (ostream& o, const Bar& b)
    {
      return o<<b.x<<' '<<b.y;
    }
    
    int main()
    {
      Foo fee = {1,2};
      Bar bee = {3,4};
    
      cout<<fee<<endl;
      cout<<bee<<endl;
    }
    But this won't
    Code:
    #include <iostream>
    using namespace std;
    
    class Foo {
      public:
        int x;
        int y;
      private:
        int z;
    
      friend ostream& operator<< (ostream&, const Foo& );
    };
    
    struct Bar {
      int x;
      int y;
      private:
        int z;
    
      friend ostream& operator<< (ostream&, const Bar& );
    };
    
    ostream& operator<< (ostream& o, const Foo& f)
    {
      return o<<f.x<<' '<<f.y;
    }
    
    ostream& operator<< (ostream& o, const Bar& b)
    {
      return o<<b.x<<' '<<b.y;
    }
    
    int main()
    {
      Foo fee = {1,2,3};
      Bar bee = {4,5,6};
    
      cout<<fee<<endl;
      cout<<bee<<endl;
    }
    struct.cpp: In function `int main()':
    struct.cpp:35: `fee' must be initialized by constructor, not by `{...}'
    struct.cpp:36: `bee' must be initialized by constructor, not by `{...}'
    Edit:
    ANSI/ISO Standard 9.4
    A structure is a class defined with the class-key struct; its members and base classes (clause 10) are public by default
    Last edited by Thantos; 12-15-2004 at 12:20 PM.

  7. #22
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    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.
    So how do you avoid the double release?
    Copy constructors and reference counting anyone?
    Code:
    class AutoReleaseIUnknown // NOT COMPLETE
    {
    public:
        AutoReleaseIUnknown(IUnknown* ptr) : ptr_(ptr), refCount(new int(1)) { }
        AutoReleaseIUnknown(const AutoReleaseIUnknown& orig) :ptr_(orig.ptr_), refCount(orig.refCount) {++(*refCount);}
        ~AutoReleaseIUnknown() { if(--(*refCount) == 0) {ptr_->Release();delete refCount;} }
        IUnknown* get() const { return ptr_; }
        IUnknown* operator->() const { return ptr_; }
    private:
        void operator=(const AutoReleaseIUnknown&);
        IUnknown* ptr_;
        int* refCount;
    };
    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).
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  8. #23
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    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.

    I've debugged it and it exits with a code of 0x00 with 0 exceptions.

  9. #24
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    Oh, thought you were having double-release problems (judging from the first line of your post ).

    [offtopic]
    Just wondering though: If you had a pointer to class X, typecasted it to void*, then deleted the void*, would the object be deleted properly?
    [/offtopic]
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  10. #25
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Dunno. Sounds kinda ignorant to do that. But in my example pTemp in the vector is the same as the local pTemp. It's just been initialized.

  11. #26
    Banned master5001's Avatar
    Join Date
    Aug 2001
    Location
    Visalia, CA, USA
    Posts
    3,685
    My apologies Thantos, I had thought that was depricated.

  12. #27
    Carnivore ('-'v) Hunter2's Avatar
    Join Date
    May 2002
    Posts
    2,879
    >>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.

    *trails off into incoherent mumbles*
    Just Google It. √

    (\ /)
    ( . .)
    c(")(") This is bunny. Copy and paste bunny into your signature to help him gain world domination.

  13. #28
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    Quote Originally Posted by Bubba
    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.

    Quote Originally Posted by Epo
    I may toss the Vector::clear() method into the destructor, just for peace of mind.
    You do that...
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  14. #29
    VA National Guard The Brain's Avatar
    Join Date
    May 2004
    Location
    Manassas, VA USA
    Posts
    903
    this might seem a little off the wall.. but I was just wondering.. what type of container does the vector class use to simulate a resizable array.. I bet it's a linked list.


    beer == good;
    Last edited by The Brain; 12-15-2004 at 03:31 PM.
    • "Problem Solving C++, The Object of Programming" -Walter Savitch
    • "Data Structures and Other Objects using C++" -Walter Savitch
    • "Assembly Language for Intel-Based Computers" -Kip Irvine
    • "Programming Windows, 5th edition" -Charles Petzold
    • "Visual C++ MFC Programming by Example" -John E. Swanke
    • "Network Programming Windows" -Jones/Ohlund
    • "Sams Teach Yourself Game Programming in 24 Hours" -Michael Morrison
    • "Mathmatics for 3D Game Programming & Computer Graphics" -Eric Lengyel

  15. #30
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    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.

Page 2 of 4 FirstFirst 1234 LastLast
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. vector of vectors containing classes
    By larne in forum C++ Programming
    Replies: 3
    Last Post: 01-13-2009, 06:19 AM
  2. classes and vectors
    By izuael in forum C++ Programming
    Replies: 10
    Last Post: 11-27-2006, 03:19 PM
  3. Vectors and custom classes
    By cunnus88 in forum C++ Programming
    Replies: 16
    Last Post: 05-12-2006, 05:11 AM
  4. vectors and classes
    By jimothygu in forum C++ Programming
    Replies: 3
    Last Post: 04-27-2003, 07:53 PM
  5. How To use vectors for custom classes
    By johnnyd in forum C++ Programming
    Replies: 14
    Last Post: 03-25-2003, 09:04 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21