tags:

views:

125

answers:

4

So I am migrating from an "owned" object model to a "managed" object model in a project I am doing. Currently to make a new Thing one does

Thing *thing = new Thing();

and to get rid of it and destroy it the owner does

delete thing

Now there are a lot of "delete thing"s out there and many of them are deleting from a superclass of Thing pointer because the superclass has a virtual destructor.

Now in the managed model there is a base class with a virtual destructor which the manager will delete. The user is supposed to call "release" on it NOT delete.

So I would like to somehow at compile time reject "delete thing" as a compile time error. Making the destructor "protected" doesn't seem to work because of the virtual destructor on the base. And it needs to be at least protected for subclasses (I think).

Anyone have any ideas?

+2  A: 

Sure; override operator delete in your class, and make it private:

struct Foo
{
private:
    void operator delete(void*); // no body (never called)
};
Pavel Minaev
This won't work if it's called through a virtual destructor.
coppro
Make the destructor private and virtual, provide empty destructor body. Make the manager class a friend of the base of the hierarchy. This way sub-classes could be managed on the head via base pointer, but only by the manager.
Nikolai N Fetissov
It will work if you do it in your base class. It will, however, restrict use of `new` too (because `new` is required to call `operator delete` is `operator new` succeeded, but constructor throwed), so you'll need to use `friend` and possibly factory methods there to work around that.
Pavel Minaev
@Pavel in this mode maybe requiring constructor calls to be changed as well is not a bad thing.
Joshua
+4  A: 

You need to make the destructors protected on both your base and your subclasses. It should work okay then.

For example the code below generates complie time errors for both delete lines.

class A
{
protected:
    virtual ~A() {}
};


class B : public A
{
protected:
    virtual ~B() {}
};

int main()
{
    B* b = new B;
    A* a = new B;

    delete b;
    delete a;

    return 0;
}
Troubadour
Yes, and then you can friend your manager so it can destruct your objects.
Keith Randall
which compiler ?I just compiled this code in Visual C++ inside a namespace with no errors
Peter Kennard
Pardon - my error two files with same name :)It does create errors. Now trying to get balance of things to work on existing class and still allow access to superclass base methods by templates.
Peter Kennard
A: 

You have a number of conflicting goals here. Are you trying to restrict use of delete on a single type, or on an entire class hieararchy? I think you need to look into using custom allocation/deallocation functions (also known as opeartor new and operator delete). In particular, you will want your object to have its own operator new and operator delete functions (possibly with extra parameters) to make sure the object gets allocated and deallocated correctly.

I won't explain the use of those functions here, as writing a correct (e.g. exception-safe, etc.) allocation/deallocation function is not the simplest of tasks, and there are a number of additional difficulties to be wary of. As a result, you're best off finding a good piece of documentation on writing them (and I don't know of one offhand, sorry).

coppro
yes, but :) I want to force the users to use a "create" and "release" instead of overidden new/delete 'cause "delete" is *after* the destructor (and new is before) with pools of objects you don't want the destructor call. When the refcount goes to 0 (you don't want to allow delete on a refcounted object either) maybe something special or delete but controlled. The purpose is to have a way to catch all the places where "delete" is used on the objects and make it a compile time error except inside the manager which will delete from the base super class.
Peter Kennard
You could try to declare the base class's delete operator as private so that it can't be used from the outside, except that the object could be deliberately deallocated with global delete ("::delete ptr"). Then you could make your manager a friend (or just have it use global delete and leave the operator undefined).
coppro
A: 

Ok - yes if I override operator delete on a base class if will cause delete to fail, however it needs to be functional as it IS called when the virtual destructor is done.

In my case I also have to make sure the base "object" interface for the equivalent of IUnknown in com has a protected destructor and constructor because the smart pointers do an implicit cast to one of these and this would be flawlessly passed to delete and compile !!! (and crash the app too)

so I have

class ObjectInterface
protected:
     ~ObjectInterface();
public:
     virtual int method1() = 0;
...
};

class ObjectBase
     : public ObjectInterface
     , public ObjectImpl // has virtual destructor on it
{
protected:
    static void operator delete(void *p) { ::operator delete(p); }
public:
     int method1(); // default impl
};

class MyObject
    : public ObjectBase
{
protected:
    MyObject();
    ~MyObject();
public:
    static SharedPointer<MyObject> create() 
    { 
        return(SharedPointer<MyObject>(new MyObject()); 
    }
};

SharedPointer<MyObject> sp = MyObject::create();
MyObject *mo = sp;

sp = NULL; // fine does actual delete inside ObjectImpl

delete sp; // compile error
delete mo; // compile error
Peter Kennard