Skip to main content
gamedev.net
🔒 Locked

OOP basic problem

Started by AndiDog Jan 31, 2007 at 10:44 AM 3 replies 1.1k views
Original Post
AndiDog
AndiDog
My objects contain the number of references which is 1 when created. The function AddRef increments this number (similar to Microsoft's COM model). I've got a Socket object and a Filter object. The socket object uses the filter and the filter uses the socket object.

// Simplified code!

#define DELETEOBJECT(obj) { if(obj->references-- == 1) delete obj; obj = 0; }

class Object
{
    Object() { references = 1; }

    int references;
};

class Filter : public Object
{
    Filter(Socket *s) { parent = s->AddRef(); }
    ~Filter() { DELETEOBJECT(parent); }

    Socket *parent;
};

class Socket : public Object
{
    ~Socket() { DELETEOBJECT(f); }

    SOCKET  handle; // used for network/Internet stuff
    Filter *f;

    void SetFilter(Filter *obj)
     {
     f = obj->AddRef();
     }
};

To explain it a bit: The filter stores a reference to the socket using AddRef(). The Socket::SetFilter() function stores a reference of the filter object.

// Simplified main function

int main()
{
    Socket *sock = new Socket; // 1 reference
    Filter *filter = new Filter; // 1 reference

    sock->SetFilter(filter); // filter now has 2 references...
    DELETEOBJECT(filter); // ... we don't use the filter in main(), so "delete" it: 1 reference remaining

    // do something

    // Here comes my problem: Socket has got 2 references
    DELETEOBJECT(sock)
}

The problem is, objects do only get deleted (= deconstructed) with references=1. But since the filter contains a reference to our socket object, the socket has references=2. So it is not deleted. In case we called "delete sock" directly, it would delete filter (and the socket reference with it) and everything was fine but that's not what it's supposed to be. Is there a solution and, does Microsoft's COM model have one for this problem???
Evil Steve
Evil Steve
The only real "Solution" would be to put an assert() in the destructor and make sure the reference count is 1 (or 0, depending how your code works).

The only way COM handles it, is you don't create objects with new, you use some factory method. That causes the object not to be allocated in your applications heap, which causes an access violation if you try to delete the object manually, rather than Release()ing it.

Also, I'd personally change:
#define DELETEOBJECT(obj) { if(obj->references-- == 1) delete obj; obj = 0; }
To:
#define DELETEOBJECT(obj) { if(--obj->references == 0) delete obj; obj = 0; }
It just seems a bit tidier to me that way. Or, better - provide a Release() method rather than relying on a macro to do the job for you, and exposing data members as public.
Sneftel
Sneftel
So it sounds like your problem is circular reference. Object A is holding the only pointer to object B and keeping it alive; object B is is holding the only pointer to object A and keeping it alive. This is a central problem with reference-counted pointers. It is solved by the idea of a "weak pointer", basically one which does not increment the reference counter. If one of the two pointers in the cycle was weak, both objects would be correctly deleted.

BTW, writing your own reference-counted object implementation to be robust and bug-free is surprisingly difficult. Using a really good premade one is almost certainly a better idea. (Specifically, you will be interested in shared_ptr and weak_ptr.)
AndiDog
AndiDog
Alright no perfect solution out there, what a pity :)

@Evil Steve: As I wrote it is just simplified code, my real code is way more complex.

I'm gonna use the "weak pointer" solution which is quite o.k.

Thanks a lot
JohnBolton
JohnBolton
The boost library has weak_ptr that you can use to break the circular dependencies.

Edit: Oh, I see that has alread been mentioned.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.