No, and (normally) no.
There are two basic approaches to subverting the reference counting mechanism:
- The scheme could be subverted if someone got a Fred*
(rather than being forced to use a FredPtr). Someone could get a
Fred* if class FredPtr has an operator*() that returns
a Fred&: FredPtr p = Fred::create(); Fred* p2 = &*p;. Yes
it's bizarre and unexpected, but it could happen. This hole could be closed
in two ways: overload Fred::operator&() so it returns a
FredPtr, or change the return type of FredPtr::operator*() so
it returns a FredRef (FredRef would be a class that simulates
a reference; it would need to have all the methods that Fred has, and
it would need to forward all those method calls to the underlying Fred
object; there might be a performance penalty for this second choice depending
on how good the compiler is at inlining methods). Another way to fix this is
to eliminate FredPtr::operator*() — and lose the corresponding
ability to get and use a Fred&. But even if you did all this, someone
could still generate a Fred* by explicitly calling
operator->(): FredPtr p = Fred::create(); Fred* p2 =
p.operator->();.
- The scheme could be subverted if someone had a leak and/or dangling
pointer to a FredPtr. Basically what we're saying here is that
Fred is now safe, but we somehow want to prevent people from doing
stupid things with FredPtr objects. (And if we could solve that via
FredPtrPtr objects, we'd have the same problem again with them). One
hole here is if someone created a FredPtr using new, then
allowed the FredPtr to leak (worst case this is a leak, which is bad
but is usually a little better than a dangling pointer). This hole
could be plugged by declaring FredPtr::operator new() as private,
thus preventing someone from saying new FredPtr(). Another hole here
is if someone creates a local FredPtr object, then takes the address
of that FredPtr and passed around the FredPtr*. If that
FredPtr* lived longer than the FredPtr, you could have a
dangling pointer — shudder. This hole could be plugged by preventing people
from taking the address of a FredPtr (by overloading
FredPtr::operator&() as private), with the corresponding loss of
functionality. But even if you did all that, they could still create a
FredPtr& which is almost as dangerous as a FredPtr*, simply by
doing this: FredPtr p; ... FredPtr& q = p; (or by passing the
FredPtr& to someone else).
And even if we closed all those holes, C++ has those wonderful pieces
of syntax called pointer casts. Using a pointer cast or two, a sufficiently
motivated programmer can normally create a hole that's big enough to drive a
proverbial truck through. (By the way, pointer casts are
evil.)
So the lessons here seem to be: (a) you can't prevent espionage no matter how
hard you try, and (b) you can easily prevent mistakes.
So I recommend settling for the "low hanging fruit": use the easy-to-build and
easy-to-use mechanisms that prevent mistakes, and don't bother trying to
prevent espionage. You won't succeed, and even if you do, it'll (probably)
cost you more than it's worth.
So if we can't use the C++ language itself to prevent espionage, are there
other ways to do it? Yes. I personally use old fashioned code reviews for
that. And since the espionage techniques usually involve some bizarre syntax
and/or use of pointer-casts and unions, you can use a tool to point out most
of the "hot spots."