As others have pointed out, you cannot guard against malicious users of Graph
. I'll assume that you don't want to, either, and that you're in the position of the Graph
author trying to prevent users of the class from misunderstanding the ownership semantics of Graph
while implementing Algorithm
(s).
You could hand out only shared_ptr<Guard>
by using the Named Constructor Idiom, which should prevent all but the most clueless of coders from attempting to delete Graph
, however, Algorithm
writers could still use Graph * mGraph
as a member variable and pass shared_ptr<>::get()
when constructing it...
To make this watertight for all but the most determined of malicious coders, you need to use the Counted Body Idiom. In short: wrap Graph*
s with a GraphHandle
class that is passed by-value and proxies Graph
's API. It's a bit like only ever creating shared_ptr<Graph>
s, but it prevents access to the raw pointer:
class Graph {
public:
// ...
void doSomething();
};
class GraphHandle {
shared_ptr<Graph> graph;
public:
explicit GraphHandle( const shared_ptr<Graph> & graph )
: graph( graph )
{
assert( graph );
}
// do NOT provide an accessor for this->graph!
// proxied API:
void doSomething() {
graph->doSomething();
}
// ...
};
class Algorithm {
// ...
GraphHandle graph;
};
This way, Algorithm
can no longer delete the Graph
.
Algorithm authors can of course still use Graph
directly. To prevent this, make Graph
private API and hide it completely behind GraphHandle
. You can see this in production in the DOM implementation of Qt.
As an aside:
Using shared_ptr
in the implementation of GraphHandle
doesn't mean GraphHandle
necessarily owns the Graph
. You can hold a shared_ptr<Graph>
outside of the GraphHandle
, or, if the rest of the code uses naked pointers, you can just pass nodelete
as the shared_ptr
s deleter:
struct nodelete {
template <typename T>
void operator()( T* ) {}
};
// ...
Graph * nakedGraph = ...;
const shared_ptr<Graph> sharedGraph( nakedGraph, nodelete() );
GraphHandle handledGraph( sharedGraph );