Is this a good reference-counting smart pointer class?

P

Protoman

Is this a good reference-counting smart pointer class?

// begin code
-------------------------------------------------------------------------------------------------------------------
class SmrtPtrDB
{
public:
SmrtPtrDB(int status=1):num(status){}
SmrtPtrDB(const SmrtPtrDB& rhs):num(rhs.num){}
~SmrtPtrDB(){}
void add(){num++;}
void sub(){num--;}
int status(){return num;}
private:
int num;
};

class NullPtr{};

template<class T>
class SmrtPtr
{
public:
explicit SmrtPtr<T>(T* obj=0):ptr(obj),DataBase(new SmrtPtrDB){}
SmrtPtr<T>(const SmrtPtr<T>& rhs):ptr(rhs.ptr),DataBase(new
SmrtPtrDB(rhs.DataBase->status())) {DataBase->add();}
~SmrtPtr<T>()
{
DataBase->sub();
if(DataBase->status()==0)
{delete ptr; delete DataBase;}
else {delete DataBase;}
}
void operator=(T* val)
{
SmrtPtr<T> temp(val);
swap(temp);
}
SmrtPtr<T>& operator=(const SmrtPtr<T>& rhs)
{
SmrtPtr<T> temp(rhs);
swap(temp);
return *this;
}
bool operator==(const SmrtPtr<T>& rhs)const {if(ptr==rhs.ptr)return
true;else return false;}
bool operator!=(const SmrtPtr<T>& rhs)const {if(ptr!=rhs.ptr)return
true;else return false;}
bool operator<=(const SmrtPtr<T>& rhs)const {if(ptr<=rhs.ptr)return
true;else return false;}
bool operator>=(const SmrtPtr<T>& rhs)const {if(ptr>=rhs.ptr)return
true;else return false;}
int status(){return DataBase->status();}
T& operator*()const {if(ptr==0)throw NullPtr();else return *ptr;}
T* operator->()const {if(ptr==0)throw NullPtr();else return ptr;}
operator T*()const {if(ptr==0)throw NullPtr();else return ptr;}
private:
void swap(SmrtPtr<T>& rhs)
{
std::swap(DataBase,rhs.DataBase);
std::swap(ptr,rhs.ptr);
}
mutable SmrtPtrDB* DataBase;
T* ptr;
};
//end of code
 
J

Juha Nieminen

Protoman said:
explicit SmrtPtr<T>(T* obj=0):ptr(obj),DataBase(new SmrtPtrDB){}
SmrtPtr<T>(const SmrtPtr<T>& rhs):ptr(rhs.ptr),DataBase(new
SmrtPtrDB(rhs.DataBase->status())) {DataBase->add();}

No, this definitely doesn't work.

You are *not* sharing the 'DataBase' instance between the 'SmrtPtr'
copies pointing to the same object. In other words, each instance of
'SmrtPtr' has its own instance of 'DataBase'. Basically there's no
difference between what you have done there and having a 'SmrtPtrDB'
instance directly as a member variable (rather than it being allocated
dynamically).

You are copying the reference count from 'rhs' to the newly-created
instance of 'DataBase' in the copy constructor. How do you think it's
going to notice if that other 'SmrtPtr' is destroyed? It retains no
connection to it whatsoever, so there's no way it can notice that it has
disappeared.

Moreover, that other 'SmrtPtr' instance is not going to notice that a
new 'SmrtPtr' instance was created to point to the same object. The
reference count of the former will be unmodified. Thus when the former
is destroyed, it will delete the object, and not the new 'SmrtPtr'
object will point to deleted memory.
 
D

douglas

  No, this definitely doesn't work.

  You are *not* sharing the 'DataBase' instance between the 'SmrtPtr'
copies pointing to the same object. In other words, each instance of
'SmrtPtr' has its own instance of 'DataBase'. Basically there's no
difference between what you have done there and having a 'SmrtPtrDB'
instance directly as a member variable (rather than it being allocated
dynamically).

  You are copying the reference count from 'rhs' to the newly-created
instance of 'DataBase' in the copy constructor. How do you think it's
going to notice if that other 'SmrtPtr' is destroyed? It retains no
connection to it whatsoever, so there's no way it can notice that it has
disappeared.

  Moreover, that other 'SmrtPtr' instance is not going to notice that a
new 'SmrtPtr' instance was created to point to the same object. The
reference count of the former will be unmodified. Thus when the former
is destroyed, it will delete the object, and not the new 'SmrtPtr'
object will point to deleted memory.

So, how do I fix it?
 
M

Marcel Müller

douglas said:
So, how do I fix it?

You either need an intrusive reference count or a manager object that is
shared by all instances of the smart pointer that point to the same object.

I prefer the first, if I have the choice.


Marcel
 
K

Kram

Is this a good reference-counting smart pointer class?

// begin code
-------------------------------------------------------------------------------------------------------------------
class SmrtPtrDB
{
public:
SmrtPtrDB(int status=1):num(status){}
SmrtPtrDB(const SmrtPtrDB& rhs):num(rhs.num){}
~SmrtPtrDB(){}
void add(){num++;}
void sub(){num--;}
int status(){return num;}
private:
int num;

};

class NullPtr{};

template<class T>
class SmrtPtr
{
public:
explicit SmrtPtr<T>(T* obj=0):ptr(obj),DataBase(new SmrtPtrDB){}
SmrtPtr<T>(const SmrtPtr<T>& rhs):ptr(rhs.ptr),DataBase(new
SmrtPtrDB(rhs.DataBase->status())) {DataBase->add();}
~SmrtPtr<T>()
{
DataBase->sub();
if(DataBase->status()==0)
{delete ptr; delete DataBase;}
else {delete DataBase;}}

void operator=(T* val)
{
SmrtPtr<T> temp(val);
swap(temp);}

SmrtPtr<T>& operator=(const SmrtPtr<T>& rhs)
{
SmrtPtr<T> temp(rhs);
swap(temp);
return *this;}

bool operator==(const SmrtPtr<T>& rhs)const {if(ptr==rhs.ptr)return
true;else return false;}
bool operator!=(const SmrtPtr<T>& rhs)const {if(ptr!=rhs.ptr)return
true;else return false;}
bool operator<=(const SmrtPtr<T>& rhs)const {if(ptr<=rhs.ptr)return
true;else return false;}
bool operator>=(const SmrtPtr<T>& rhs)const {if(ptr>=rhs.ptr)return
true;else return false;}
int status(){return DataBase->status();}
T& operator*()const {if(ptr==0)throw NullPtr();else return *ptr;}
T* operator->()const {if(ptr==0)throw NullPtr();else return ptr;}
operator T*()const {if(ptr==0)throw NullPtr();else return ptr;}
private:
void swap(SmrtPtr<T>& rhs)
{
std::swap(DataBase,rhs.DataBase);
std::swap(ptr,rhs.ptr);}

mutable SmrtPtrDB* DataBase;
T* ptr;};

//end of code

It seems like you're design is to create a database that keep track of
addresses being referenced by pointers in a class. That way you can
know how many references there are to a memory address. The problem is
you create a new copy of the database for each pointer, you need to
look into the Singleton method for the database, and use the factory
method to generate pointers. This factory can either contain or
reference the Database and that way you ensure only one shared
database.
 
D

douglas

It seems like you're design is to create a database that keep track of
addresses being referenced by pointers in a class. That way you can
know how many references there are to a memory address. The problem is
you create a new copy of the database for each pointer, you need to
look into the Singleton method for the database, and use the factory
method to generate pointers. This factory can either contain or
reference the Database and that way you ensure only one shared
database.- Hide quoted text -

- Show quoted text -

I know the Singleton pattern, but what's the factory pattern? Can you
give me an example?
 
D

douglas

  Share the reference counter with all the copies, rather than
replicating it.

Or could I modify the classes to have a mutable ref count variable,
and have the SmrtPtr class add and subtract from that, and free the
ptr when that var hits 0. Would that be intrusive ref counting?
 
J

James Kanze

Share the reference counter with all the copies, rather than
replicating it.
[/QUOTE]
Or could I modify the classes to have a mutable ref count
variable, and have the SmrtPtr class add and subtract from
that, and free the ptr when that var hits 0. Would that be
intrusive ref counting?

That's what is meant by intrusive reference counting, yes.

The version having the separate counter object (from Barton and
Nackman, for example) has the advantage that it works with all
types of objects, even ones in the standard or some third party
library. On the other hand, it is extremely fragile---it's far
too easy to end up with two counter objects. For this reason, I
would avoid it at all cost in critical code. The invasive
version (from Scott Meyers, for example) typically requires that
the pointed to object ultimately derive from a base class
containing the counter. This means that you can't use it on
existing classes directly (but it's trivial to wrap an existing
class so that you can use it); it also has implications when
multiple inheritance is involved. On the other hand, it's
fairly robust. (As robust as reference counting can be.)
 
J

Juha Nieminen

Christian said:
In addition to what others have said, I wonder why your class throws an
exception on implicit conversion of a null pointer. Why should this be
illegal?

I bet that what he wanted was overload operator*.
 
D

douglas

  I bet that what he wanted was overload operator*.

I'm relatively new to this as you've guessed; I've only been coding
for two years, for fun. I'm also 17 years old and in community college.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top