-
December 28th, 2003, 06:56 AM
#1
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;
}
}
};
-
December 28th, 2003, 07:03 AM
#2
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
-
December 28th, 2003, 09:24 AM
#3
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?
-
December 28th, 2003, 10:02 AM
#4
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
-
December 28th, 2003, 12:45 PM
#5
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?
-
December 28th, 2003, 08:00 PM
#6
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
-
December 29th, 2003, 01:23 AM
#7
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?
-
December 29th, 2003, 01:47 AM
#8
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."
-
December 29th, 2003, 02:22 AM
#9
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.
-
December 29th, 2003, 02:41 AM
#10
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."
-
December 29th, 2003, 03:39 AM
#11
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.
-
December 29th, 2003, 05:41 AM
#12
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...
-
December 29th, 2003, 09:21 AM
#13
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|