9

I have a resource_manager class which maintains a std::vector<boost::shared_ptr<resource> > internally. resource_manager is a friend class of resource. I want resources to only be created/deleted by resource_manager, so I made its constructors private (which works ok).

However, if I make the destructor private, the code doesn't compile because the destructor is called by boost::shared_ptr, which is not a friend of resource. I am thinking of enforcing the "do not delete by clients" rule by only returning only const resource* from the resource_manager, but somehow I am not satisfied with the security this method provides (what if a client somehow happens across a pointer to non-const?)

Apart from the obvious solution of not using shared_ptr, do you have any workaround / better solution to my problem?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Dan Nestor
  • 2,441
  • 1
  • 24
  • 45
  • " client somehow happens across a pointer to non-const" the only way they could do that is if they cast using const_cast. From a "security" perspective there is no difference between raw pointers and shared_ptr, the difference lies in life-time management, of which I am unsure how you would handle without shared_ptr. – ronag Nov 20 '11 at 15:31
  • 4
    A `const resource*` does not mean that you can't delete it. – UncleBens Nov 20 '11 at 15:36
  • @UncleBens: It doesn't? I thought the compiler should not allow you to call a non-const function on a const! Am I mistaken? Or doesn't this rule apply to the destructor? – Dan Nestor Nov 20 '11 at 18:58
  • 1
    The rule doesn't apply to destructors, because otherwise you couldn't destruct objects that are `const`. – MSalters Nov 21 '11 at 08:20

2 Answers2

14

You can pass a custom deleter to the shared pointer. So just create a deleter functor or function (up to you) which in turn is a friend of your class:

class Secret
{
  ~Secret() { }
  friend class SecretDeleter;
  friend void SecretDelFunc(Secret *);
};

class SecretDeleter
{
public:
  void operator()(Secret * p) { delete p; }
};

void SecretDelFunc(Secret * p) { delete p; }

std::shared_ptr<Secret> sp1(new Secret, SecretDeleter());
std::shared_ptr<Secret> sp2(new Secret, SecretDelFunc);
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    Btw, you should instantiate the `SecretDeleter`. – Xeo Nov 20 '11 at 15:53
  • 1
    @Xeo: Done. I guess a free function would even be easier than a functor class. I'll add that too. (Actually, in C++11 there's an arcane third option of making a special allocator that's a friend and using `std::allocate_shared`.) – Kerrek SB Nov 20 '11 at 15:55
  • Is there a less painful way than create standalone delete objects and/or functions. I suppose it is a pretty common idiom – uuu777 Jan 17 '15 at 20:53
  • @KerreK SB. I do not know about your experience. We have another 2000+ software engineers besides me and I would like (a) to be sure that no-one can call delete on object managed by shared_ptr, and (b) do not type custom deleter for every object I write, and (c) use performance and safety advantages of make_shared. Maybe it is specific for large shops. – uuu777 Jan 19 '15 at 18:25
  • @zzz777: How does a simple private constructor not suffice for that sort of stuff? You're defending against Murphy, not Machiavelli. – Kerrek SB Jan 19 '15 at 23:18
  • @Kerrek SB. I am missing something big. Naturally all constructors are private in such environment, but how a private constructor can help prevent unintended deletions completely misses me, I am sorry. – uuu777 Jan 20 '15 at 05:14
  • @zzz777: I just think that if you use modern C++ idioms throughout your codebase, then occurrences of raw `delete`s are rare enough that such erroneous deletions wouldn't be "unintentional": Both `shared_ptr` and `delete` are special-purpose tools in C++11 that should be used sparingly, and the intersection is even smaller (e.g. having long-lived raw pointers to shared pointees). So I'm not sure whether there's a real problem worth solving here, and whether there are lots of real bugs caused by this (which aren't caused by generally unclear lifetime semantics in the code style). – Kerrek SB Jan 20 '15 at 09:16
  • @Kerrek SB. So you agree with me on substance. Now about practical implications of using "modern C++". One has no chance to know about 99.9999% of real bugs in any real software development company. And on top of that you have no chance to learn about any real bug on any feature that is NOT used because people who make the decisions feel it is unnecessary cumbersome. There is nothing new here - folks designing c++ like their stuff to be very cumbersome. – uuu777 Jan 21 '15 at 15:36
1

Perhaps declare shared_ptr<resource> as a friend? shared_ptr doesn't call the constructor, and should only destruct if your resource manager releases the pointer before all clients have destroyed their shared_ptrs. This won't allow clients to break the protection, but will allow clients to keep a resource alive against the resource_manager's "will."

01d55
  • 1,872
  • 13
  • 22