CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 13 of 13
  1. #1
    Join Date
    Nov 2003
    Posts
    1,405

    Jossuttis smart pointer for reference counting.

    This is an implementation of a smart pointer for reference counting published in this book by Josuttis,

    http://www.josuttis.com/libbook/index.html

    My question is concerning the count variable. Why is it a pointer to a long? If it was just a plain long you wouldn't have to allocate space on the heap for it. It would be more efficient. What's the reason for this?
    Code:
    /* class for counted reference semantics
     * - deletes the object to which it refers when the last CountedPtr
     *   that refers to it is destroyed
     */
    template <class T>
    class CountedPtr {
      private:
        T* ptr;        // pointer to the value
        long* count;   // shared number of owners
    
      public:
        // initialize pointer with existing pointer
        // - requires that the pointer p is a return value of new
        explicit CountedPtr (T* p=0)
         : ptr(p), count(new long(1)) {
        }
    
        // copy pointer (one more owner)
        CountedPtr (const CountedPtr<T>& p) throw()
         : ptr(p.ptr), count(p.count) {
            ++*count;
        }
    
        // destructor (delete value if this was the last owner)
        ~CountedPtr () throw() {
            dispose();
        }
    
        // assignment (unshare old and share new value)
        CountedPtr<T>& operator= (const CountedPtr<T>& p) throw() {
            if (this != &p) {
                dispose();
                ptr = p.ptr;
                count = p.count;
                ++*count;
            }
            return *this;
        }
    
        // access the value to which the pointer refers
        T& operator*() const throw() {
            return *ptr;
        }
        T* operator->() const throw() {
            return ptr;
        }
    
      private:
        void dispose() {
            if (--*count == 0) {
                 delete count;
                 delete ptr;
            }
        }
        
    };

  2. #2
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    If it were a long, every pointer instance would have its own count. But you want a count per object that is pointed to, not per pointer.
    All the buzzt
    CornedBee

  3. #3
    Join Date
    Nov 2003
    Posts
    1,405
    Yes of course thank you! I thought there was just one CountedPtr object for every ptr object, but I see now there can be many and they must all share the same count.

    This is all new to me. What I had in mind was a class with a count. When you take a copy of a pointer to an object of this class the count gets incremented. Each copied pointer can be stored at different places (like in collections) and each time the destructor belonging to a copied pointer is called the count is decremented until it is 0 when the object it's destructed for real. Basically each copied pointer has the right to give rise to one destructor call. When all copied pointers have exercised this right the object finally gets destructed.

    Could this work as a reference counting mechanism?

  4. #4
    Join Date
    Nov 2003
    Location
    Vienna, Austria
    Posts
    212
    It would work, but only on classes that are made to work with the pointer. Josutti's approach (or the Boost approach, it is the same) works on every type.
    All the buzzt
    CornedBee

  5. #5
    Join Date
    Nov 2003
    Posts
    1,405
    Originally posted by CornedBee
    It would work, but only on classes that are made to work with the pointer. Josutti's approach (or the Boost approach, it is the same) works on every type.
    I was looking for a more effective approach. Although general the Josutti/Boost approach introduces an extra level of indirection and each CountedPtr object holds two extra pointers and allocates count objects on the heap.

    The approach I suggested would just add a counter to each object. I was thinking maybe I could wrap this functionality into a class CountRef or something. A class would just have to derive itself from CountRef to inherit this simple reference counting capability. But maybe there are problems with this?

  6. #6
    Join Date
    Sep 2002
    Posts
    1,747
    Normally, you don't want to add an inheritance relationship when it is not needed. You are left dragging around type-conversion semantics when it is unneeded, it is an intrusive method that introduces unnecessary coupling, and the advantages appear only in the use case where many different instances usually have only 1 or 2 smart pointers looking at them (where smart pointers are less useful anyway and optimisation would point to possibly naked pointers if profiling showed them necessary since you are not needing to track a lot of separate references). Adapting a counter to each object is a viable alternative that can be done through containment. Having the smart pointer point to the adapter will give very much the same properties, though. Particularly since you usually use smart pointers on objects that are created on the free store outside the management of the smart pointer (so you would still require an indirection on every count increment / decrement whether inherit or contain adapted). You can try workarounds like storing a static vector of instance counts (ie. at some example time, the first instance has 3 smart pointers, the 2nd instance has 8, etc...), but all you are doing is moving the instance indirection to the storage of a vector iterator / index id for the instance. Alternatively, reference linking moves the instance indirection to link maintenance.

    So, you can optimise a smart pointer's functionality to move the indirections around to the least used areas of your usage pattern, but you should be certain that your usage pattern does match the type of optimisation you are building in (always profile!). The smart pointer you presented from Josuttis matches a common usage pattern and is very nonintrusive, which is why it used so commonly.
    */*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/

    "It's hard to believe in something you don't understand." -- the sidhi X-files episode

    galathaea: prankster, fablist, magician, liar

  7. #7
    Join Date
    Nov 2003
    Posts
    1,405
    Originally posted by galathaea
    The smart pointer you presented from Josuttis matches a common usage pattern and is very nonintrusive, which is why it used so commonly.
    Thank you. By thinking about different alternatives I'm starting to appreciate the Josuttis' CountedPtr and understand it better.

    The problem I have to overcome is that if I create objects on the heap using new and then store the same pointers in many collections I will have an error eventually. There seems to be no way to delete a collection or remove pointers from a collection without invoking calls to the corresponding object destructors.

    I'll have to live with this and the minimal way to handle it seems to be to introduce indirect pointers. It's basically CountedPtr but without the reference counting part. I'm calling it WrapperPtr.

    I wrap the 'naked' object pointers into WrapperPtr objects and store them in many collections. When a collection gets deleted the WrapperPtr objects get deleted with it, but the 'naked' pointer each contains is left untouched and the object it points to lives on.

    Am I correct in my assumptions? I'm still responsible for deleting my objects explicitly of course at some point, but by using WrapperPtr it seems I can accomodate the object pointers in collections without risking destruction of the objects when the collections are destructed?

  8. #8
    Join Date
    Nov 2002
    Location
    Los Angeles, California
    Posts
    3,863
    I am not sure I follow you. If you just store the raw pointers in containers, then the object pointed to will not have the destructor
    called when the container is deleted or emptied or whatever,
    unless you explicitly delete the pointer. Your wrapped pointer
    gains you nothing; I don't understand how it is any different than
    just putting the raw pointers in the containers.

    The idea behind smart pointers is to
    1. Declare ownership. As long even one instance of the smart pointer exist, the pointed to object can not be deleted. There
    can be no dangling pointers.

    2. Provide some level of garbage collection. When there are no
    more smart pointers claiming ownership to a pointed to object, then it will be deleted.

    If I were you then I would consider safety before performance.
    Don't try and optimize unless you know there is a problem and you know where the bottleneck is.
    Wakeup in the morning and kick the day in the teeth!! Or something like that.

    "i don't want to write leak free code or most efficient code, like others traditional (so called expert) coders do."

  9. #9
    Join Date
    Nov 2003
    Posts
    1,405
    Originally posted by souldog
    If you just store the raw pointers in containers, then the object pointed to will not have the destructor called when the container is deleted or emptied or whatever,
    Gosh do I hate to be a newbie, but I read things in the STL library documentation such as "....... Containers destroy their internal copies of elements when these elements are removed from the container ......." I took this to mean that if the element is a pointer the object it points to gets destructed. Anyway I'm glad this was a misunderstanding, it makes life so much easier. I'm coming from Java so memory management scares the crap out of you.

  10. #10
    Join Date
    Nov 2002
    Location
    Los Angeles, California
    Posts
    3,863
    Originally posted by _uj
    I'm coming from Java so memory management scares the crap out of you.
    And it should. That is why you should use the smart pointers and not worry too much about performance unless there is actually a problem.
    Wakeup in the morning and kick the day in the teeth!! Or something like that.

    "i don't want to write leak free code or most efficient code, like others traditional (so called expert) coders do."

  11. #11
    Join Date
    Nov 2003
    Posts
    1,405
    I'm going to use smart pointers.

    One question about the very basics. What you said about vectors is that true for ordinary arrays too? For example if I use delete[] on an array of pointers (to objects allocated on the heap), does this mean the destructors of the objects pointed to get called?

    If this is so, array and vector would behave differently! I haven't found a straight answer to this anywhere.

  12. #12
    Join Date
    May 2000
    Location
    KY, USA
    Posts
    18,652
    Originally posted by _uj
    One question about the very basics. What you said about vectors is that true for ordinary arrays too? For example if I use delete[] on an array of pointers (to objects allocated on the heap), does this mean the destructors of the objects pointed to get called?

    If this is so, array and vector would behave differently! I haven't found a straight answer to this anywhere.
    No...it is not. You have to delete the objects explicitly on your own...

  13. #13
    Join Date
    Nov 2003
    Posts
    1,405
    Originally posted by Andreas Masur
    You have to delete the objects explicitly on your own...
    This is starting to make sense. It doesn't matter where you store a pointer to an object residing on the heap. The object will never get destructed just because the pointer happens to be stored in a variable or array or collection or anything else that gets destructed. You'll always have to destruct the object explicitly by using delete on the pointer.

    Thank you for your patience. Now this is obvious to me too. /Ulrika J

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured