Question

I have a module (dll/so) which exports a single factory function which returns an object then called. Using the interface (pure-virtual) users of the module can then create different objects. All object creation is through the interface, and therefore takes place using the run-time associated with my module and not the application run-time.

As the allocation is taking place inside the module, the deletion also needs to be, because if the application has a different run-time to my module, gpf/segfault time. so I have a "release" member, which performs a self-delete.

void foo::release(void) 
{
    delete this;
}

Everything is working fine, but it does require that the user of the module behaves.

My question is:

  • Is it possible to stop someone issuing a delete on my object directly (Or redirect it to delete from my modules memory pool)?
  • If not as a backup plan, is it possible to detect this in my object, so I can throw an assertion to force good behavior?

E.G:

iFoo* foo = createFoo ();
foo->release();           // Allowed and expected
delete foo;               // Disallowed
Was it helpful?

Solution

In the comments to the OP, @dave made a suggestion to declare the destructors in your pure interfaces to be protected instead of public. This would outright prevent external code (that is, external to implementation classes) from invoking delete.

For example:

class IFoo
{
protected:
    virtual ~IFoo() { }

public:
    virtual void release() = 0;
};

class Foo : public IFoo
{
public:
    void release() override
    {
        delete this;
    }
};

IFoo* createFoo()
{
    return new Foo();
}

int main()
{
    auto foo = createFoo();
    foo->release();  // Expected
    delete foo;      // Cannot access protected destructor of IFoo

    Return 0;
}

Since your factory functions only expose the pure interfaces, this approach doesn't break down if an implementation class happens to provide a public destructor. If Foo declared a public destructor, a compiler error would still occur in main because main doesn't know that it's actually dealing with a Foo.

OTHER TIPS

On Edit: This approach only makes it more difficult for users to delete the resource - it doesn't outright prevent it. (I'll refrain from deleting this answer, since it might still be useful.)

If you really want keep someone from invoking delete on your objects, make it illegal for them to do so - return a value type from your factory function.

The value type could be a thin wrapper around the actual object and could provide pointer semantics, a la smart pointers.

A rough example:

class IFoo
{
public:
    virtual ~IFoo() { }

    virtual void release() = 0;
};

class Foo : public IFoo
{
public:
    Foo() { }

    void release() override
    {
        delete this;
    }
};

// Value type with pointer semantics
template <class T>
class Undeletable
{
private:
    T* m_resource;

public:
    Undeletable(T* resource)
        : m_resource(resource)
    {
    }

    T* operator->()
    {
        return m_resource;
    }
};

// Old factory function
IFoo* createFoo()
{
    return new Foo();
}

// New factory function
Undeletable<IFoo> createSafeFoo()
{
    return Undeletable<IFoo>(createFoo());
}

int main()
{
    auto foo = createFoo();
    foo->release();  // Expected
    delete foo;      // Possible but DO NOT WANT

    auto safeFoo = createSafeFoo();
    safeFoo->release(); // Expected
    delete safeFoo;     // Compiler says NOPE

    return 0;
}

Unfortunately, this only obfuscates the fact that the user can still delete the resource. For example:

delete safeFoo.operator->(); // Deletes the resource
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top