Boost logo

Boost :

Subject: [boost] Age-based reference counting solves the problem of cyclic references?
From: Achilleas Margaritis (axilmar_at_[hidden])
Date: 2009-12-10 09:26:07


Hello all. I've been playing a little with reference counting ideas, and I
came up with an idea that might solve the problem of cyclic references: if a
smart pointer remembers all the objects it has touched, and if it knows its
age and the age of the objects it has pointed to, the smart pointer can
delete the objects if its age is older than the objects it has pointed to.

Here is an example:

#include <list>
#include <iostream>
using namespace std;

unsigned age = 0;

class aged {
public:
    aged() : m_age(-1), m_deleted(0) {
    }

    aged(const aged &c) : m_age(-1), m_deleted(0) {
    }

    void operator = (const aged &a) {
    }

private:
    unsigned m_age;
    unsigned m_deleted;
    template <class T> friend class ptr;
};

template <class T> class ptr {
public:
    explicit ptr(T *value = 0) : m_value(value), m_age(++age) {
        _acquire();
    }

    ptr(const ptr<T> &p) : m_value(p.m_value), m_age(++age) {
        _acquire();
    }

    ~ptr() {
        _release();
        m_value = 0;
        _expire();
    }

    T *operator ->() const {
        return m_value;
    }

    void reset() {
        _release();
    }

    ptr<T> &operator = (const ptr<T> &p) {
        _release();
        m_value = p.m_value;
        _acquire();
        return *this;
    }

private:
    T *m_value;
    unsigned m_age;
    list<T *> m_remembered;

    void _acquire() {
        if (m_value && m_value->m_age > m_age) m_value->m_age = m_age;
    }

    void _release() {
        if (m_value && m_value->m_age >= m_age)
m_remembered.push_back(m_value);
    }

    void _expire() {
        for(list<T *>::iterator it = m_remembered.begin(); it !=
m_remembered.end(); ++it) {
            T *obj = *it;
            if (obj->m_age >= m_age && !obj->m_deleted) {
                ++obj->m_deleted;
                obj->~T();
                --obj->m_deleted;
                if (!obj->m_deleted) ::operator delete(obj);
            }
        }
    }
};

class Foo;

class Bar : public aged {
public:
    ptr<Foo> m_foo;

    Bar() {
        cout << "Bar()\n";
    }

    ~Bar() {
        cout << "~Bar()\n";
    }
};

class Foo : public aged {
public:
    ptr<Bar> m_bar;

    Foo() {
        cout << "Foo()\n";
    }

    ~Foo() {
        cout << "~Foo()\n";
    }
};

int main() {
    {
        ptr<Foo>foo0;
        {
            ptr<Foo> foo1(new Foo);
            foo1->m_bar = ptr<Bar>(new Bar);
            foo1->m_bar->m_foo = foo1;
            ptr<Bar> bar1(foo1->m_bar);
            {
                ptr<Foo> foo2 = foo1;
                foo2->m_bar = ptr<Bar>(new Bar);
                foo0 = foo1;
                foo1.reset();
            }
        }
    }
    return 0;
}

While there is a referential cycle (foo->bar->foo), all objects are
eventually deleted.

While the above example doesn't use reference counting, it can be combined
with reference counting to solve the issue of cyclic references: objects
that their reference count reaches 0 are deleted normally; objects with
cycles remain in memory until the oldest pointer to them goes out of scope.
In most cases,

What do you think?


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk