I just programmed this up for a friend who’s wrestling with good ol’ dynamic allocation issues in C++. It’s a “smart pointer” class that takes all the hard work out of managing dynamic memory. You can find this kind of stuff elsewhere on the net too, but here’s my take:
smartpointer.h
#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H
//====================================================================
// smartpointer.h
//
// 2009.04.12 by Abe.Pralle (at) gmail.com
// This code is released to the public domain.
//====================================================================
#include <iostream>
using namespace std;
template <class ObjectType>
class SharedReference
{
public:
int count;
ObjectType* object;
SharedReference( ObjectType* object )
{
this->object = object;
count = 1;
}
void retain() { ++count; }
void release()
{
if (--count == 0)
{
delete object;
delete this;
}
}
};
template <class ObjectType>
class Ref
{
private:
SharedReference<ObjectType>* ref;
public:
Ref()
{
ref = 0;
}
Ref( ObjectType* object )
{
ref = new SharedReference<ObjectType>(object);
}
Ref( const Ref<ObjectType>& other )
{
ref = 0;
operator=(other);
}
~Ref()
{
if (ref) ref->release();
}
void operator=( const Ref<ObjectType>& other )
{
if (other.ref) other.ref->retain();
if (ref) ref->release();
ref = other.ref;
}
void operator=( ObjectType* object )
{
if (ref) ref->release();
ref = new SharedReference<ObjectType>( object );
}
ObjectType* operator*()
{
if (ref) return ref->object;
else return (ObjectType*) NULL;
}
ObjectType* operator->()
{
if (ref) return ref->object;
else return (ObjectType*) NULL;
}
};
#endif // SMARTPOINTER_H
smartpointer.cpp
#include <iostream>
using namespace std;
#include "smartpointer.h"
class Fruit
{
public:
const char* name;
Fruit( const char* name ) { this->name = name; }
~Fruit() { cout << " [deleting " << name << "]" << endl; }
};
Ref<Fruit> make_fruit();
void mess_with_ref( Ref<Fruit> fruit );
int main()
{
Ref<Fruit> fruit = make_fruit();
cout << "I have an " << fruit->name << endl;
fruit = new Fruit( "grapes" );
cout << "Now it's some " << fruit->name << endl;
mess_with_ref( fruit );
cout << "Still " << fruit->name << "!" << endl;
Ref<Fruit> fruit2 = fruit;
cout << "If I have two references to " << fruit->name << endl;
fruit = new Fruit( "strawberries" );
cout << "And I set one of them to some " << fruit->name << endl;
cout << "Then I still have some " << fruit2->name << endl;
return 0;
}
Ref<Fruit> make_fruit()
{
Ref<Fruit> apple = new Fruit( "apple" );
Ref<Fruit> orange = new Fruit( "orange" );
return apple;
}
void mess_with_ref( Ref<Fruit> fruit )
{
fruit = new Fruit( "some kind of weird pear" );
Fruit* f = *fruit;
cout << "Got the object pointer back to " << f->name << endl;
}
Test program output:
[deleting orange] I have an apple [deleting apple] Now it's some grapes Got the object pointer back to some kind of weird pear [deleting some kind of weird pear] Still grapes! If I have two references to grapes And I set one of them to some strawberries Then I still have some grapes [deleting grapes] [deleting strawberries]
April 13, 2009 at 4:33 am
Hi!
I think it’s fun to write such a smart pointer for educational purposes. But the shared_ptr of existing libraries (Boost, TR1) but also the future C++ standard library is a bit more flexible with respect to pointer type conversions and custom deleters. You should favor these advanced implementations over homebrewn smart pointers that are not so well-tested.
Also: Never ever write a using directive in a header file that pulls many names into the global scope. Just say no.
Cheers!
P.
April 13, 2009 at 6:19 pm
It will be a cold day in hell before I bring boost into a project for simple smart pointers.
April 14, 2009 at 9:21 am
Personally, if I do need to use C or C++, I’d rather manage the memory myself. Though there are plenty of situations where you need help and this sort of thing is useful.
Joe