0
votes

I have a leaky code like this:

void f() {
  SomeClass *sc = new SomeClass;
  ...
  if (...) {
    g(sc); // g() takes ownership of memory pointed to by sc
  }
  ...
  sc->SomeMethod();
  ...
}

I'd like to eliminate leaks by using smart pointers, thought of the following solution:

void f() {
  smart_ptr<SomeClass> sc(new SomeClass);
  ...
  if (...) {
    g(sc.release()); // ownership is taken
  }
  ...
  sc->SomeMethod(); // sc points to the allocated memory regardless of whether ownership was taken or not
  ...
} // the memory is freed if ownership was not taken

Tried to use std::unique_ptr and std::shared_ptr. std::unique_ptr points to the null pointer after release, std::shared_ptr has no release() at all. Manual increase of reference counter of std::shared_ptr would have helped, but as I understand correctly there is no such an ability too.

Thought of using std::unique_ptr in addition to the raw pointer leaving the code as is and just calling release() each time the raw pointer is passed to the function that takes ownership, but it is a very muddy solution.

I wonder if there is a smart pointer from the standard library that fit for my goals, or maybe there is an ideom or common trick how to do in my case or may I missing something?

3
Just use a unique_ptr and a raw pointer, it's the simplest solution. You do need to ensure that g() doesn't delete the object before you call SomeMethod() via the raw pointer.Praetorian
Yes, the solution is simplest but error-prone. The code is open source, I'd like to use a single variable. The real body of f() is large, the pointer is frequently used and the ownership is transferred several times.ayartsev

3 Answers

3
votes

You can retain a local, non-owning view with just a little more code:

void g(std::unique_ptr<Foo> p);     // takes ownership

int main()
{
    auto p = std::make_unique<Foo>();
    Foo & local_view = *p;

    if (/* condition */) { g(std::move(p)); }

    local_view.do_stuff();
}
0
votes

I don't understand what you mean about g taking ownership, because after g exits you are still going to use the pointer. If you always want to use the pointer after g, it makes more sense to pass a raw pointer to g and deallocate the pointer at the end of the block.

If you do want g to take ownership, and want to call the SomeMethod only if your pointer still exists, pass a raw pointer to g and have it return a raw pointer. If the pointer gets deallocated in g, return nullptr and check for that before the SomeMethod call.

0
votes

You can use shared_ptr with equal effect. shared_ptr does not have a release() function but it has a reset() function, which is what I think you are looking for. However, you have to wait until after the call to SomeClass::SomeMethod() to call sc.reset().

void g(shared_ptr<SomeClass> ptr);

void f()
{
   shared_ptr<SomeClass> sc(new SomeClass);

   if (/* check for condition */)
   {
      g(sc);
   }

   sc->SomeMethod();
   sc.reset();
}