Skip to content

Instantly share code, notes, and snippets.

@erikkaplun
Created March 19, 2012 18:01
Show Gist options
  • Save erikkaplun/2121814 to your computer and use it in GitHub Desktop.
Save erikkaplun/2121814 to your computer and use it in GitHub Desktop.
smartpointer (C++)
#include <iostream>
#include <vector>
#include <cassert>
#include <string>
using std::vector;
using std::cout;
using std::endl;
using std::string;
#define DEBUG
#include "smartpointer.h"
struct Spaceship : public smart<Spaceship> {
Spaceship() {}
};
struct Missile : public smart<Missile> {
Spaceship::ptr spaceship;
Missile(Spaceship* s) : spaceship(s) {}
};
void test1();
void test2(Spaceship *);
void test3();
void test4();
void test5();
int main() {
LOG("-- MAIN {");
test1();
LOG("-- TEST2 {");
Spaceship::ptr ship = new Spaceship();
test2(ship);
test3();
test4();
test5();
LOG("-- } MAIN");
// the ship passed to test2 will be freed here
}
void test1() {
LOG("-- TEST1 {");
Spaceship::ptr ss = new Spaceship();
assert(ss->numrefs() == 1);
Missile::ptr missile1 = new Missile(ss);
assert(ss->numrefs() == 2);
Missile::ptr missile2 = new Missile(ss);
assert(ss->numrefs() == 3);
delete ss;
assert(ss.wrapped == null);
assert(missile1->spaceship == null);
assert(missile2->spaceship == null);
LOG("-- } TEST1");
// the missiles get auto-deleted, but the spaceship has
// already been deleted and won't be deleted twice.
}
void test2(Spaceship* ship) {
Missile::ptr missile = new Missile(ship);
// Missile will be freed when the pointer to it goes out of scope
// but the ship passed in to this function will not be freed
LOG("-- } TEST2");
}
void test3() {
LOG("-- TEST3 {");
Spaceship::ptr ship = new Spaceship();
Missile::ptr missile1 = new Missile(ship);
missile1->spaceship = null;
assert(missile1->spaceship == null);
assert(ship != null);
LOG("-- } TEST3");
}
void test4() {
LOG("-- TEST4 {");
Spaceship::ptr ship = new Spaceship();
ship = null; // Spaceship instance will be freed
LOG("-- } TEST4");
}
void test5() {
LOG("-- TEST5 {");
Spaceship::ptr ship = new Spaceship();
Spaceship::ptr ship_alias = ship;
assert(ship->numrefs() == 2);
ship_alias = null;
assert(ship->numrefs() == 1);
ship_alias = (Spaceship*) ship;
assert(ship->numrefs() == 2);
ship_alias = null;
assert(ship->numrefs() == 1);
ship_alias = ship;
assert(ship->numrefs() == 2);
Spaceship::ptr ship_alias2 = ship;
assert(ship->numrefs() == 3);
ship_alias2 = null;
assert(ship->numrefs() == 2);
ship_alias = ship_alias2;
assert(ship->numrefs() == 1);
try {
(*ship_alias).numrefs();
assert(0);
} catch (const char *e) {
cout << "dereferencing a null smartpointer throw as expected" << endl;
}
try {
ship_alias->numrefs();
assert(0);
} catch (const char *e) {
cout << "dereferencing a null smartpointer throw as expected" << endl;
}
LOG("-- } TEST5");
}
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__
#include <iostream>
#include <vector>
#include <typeinfo>
#include <string>
using std::vector;
using std::cout;
using std::endl;
using std::string;
#ifdef DEBUG
#define IS_DEBUG 1
#else
#define IS_DEBUG 0
#endif
#define LOG(x) if (IS_DEBUG) { cout << x << endl; }
#define TYPEDLOG(x) if (IS_DEBUG) { cout << typeid(*this).name() << "\t" << x << endl; }
const int null = 0;
template <class T> class smartpointer;
template <class T>
class smart {
friend class smartpointer<T>;
private:
typedef vector<smartpointer<T>*> ptrs_t;
ptrs_t ptrs;
public:
typedef smartpointer<T> ptr;
smart() {
TYPEDLOG("new");
}
virtual ~smart() {
TYPEDLOG("delete");
for (typename ptrs_t::iterator it = this->ptrs.begin(); it < this->ptrs.end(); ++it) {
TYPEDLOG("nullifying pointer");
(*it)->wrapped = null;
}
}
unsigned int numrefs() {
return this->ptrs.size();
}
};
template <class T>
class smartpointer {
friend class smart<T>;
public: // for assertions in demo.cpp
T* wrapped;
public:
smartpointer(T* wrapped) : wrapped(wrapped) {
// TYPEDLOG("new");
this->point();
}
smartpointer(const smartpointer& other) : wrapped(other.wrapped){
// TYPEDLOG("new");
this->point();
}
virtual ~smartpointer() {
TYPEDLOG("delete");
this->clear();
}
T* operator ->() const {
if (this->wrapped == null)
throw "Empty smartpointer";
return this->wrapped;
}
T& operator *() const {
return *(this->operator->());
}
operator T*() const {
return this->wrapped;
}
void clear() {
if (this->wrapped)
this->unpoint();
}
smartpointer& operator= (T* ptr) {
if (ptr != this->wrapped) {
this->clear();
if (ptr != null) {
this->wrapped = ptr;
this->point();
}
}
return *this;
}
smartpointer& operator= (const smartpointer &other) {
return this->operator= (other.wrapped);
}
private:
void point() {
this->wrapped->ptrs.push_back(this);
}
void unpoint() {
smart<T>* ptr = this->wrapped;
int found = 0;
for (typename smart<T>::ptrs_t::iterator it = ptr->ptrs.begin();
it < ptr->ptrs.end();
++it)
{
if (*it == this) {
ptr->ptrs.erase(it);
found = 1;
break;
}
}
assert(found);
if (ptr->ptrs.empty()) {
TYPEDLOG("auto-deleting");
delete ptr;
} else {
TYPEDLOG("only forgetting");
}
// C++ doesn't automatically null out the pointer
this->wrapped = null;
}
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment