Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a coding style absolutely guarantee no memory leaks?

Never using

new
delete
release

and preferring to use

std::make_unique
std::unique_ptr
std::move
reset (redundant)

should morally result in no memory leaks: new'ed pointers are only ever created inside smart pointers, from which they can never escape, because we have disallowed use of release.

One may therefore be tempted into using this coding style, and then never bother checking for memory leaks again - no matter where exceptions may be thrown from, the RAII semantics of the smart pointers should always clean up any dangling pointers as the stack is unwound.

Except C++ is full of nasty surprises. From experience of having my assumptions repeatedly smashed by gotw, I can't help but think that there might be some corner-case which manages to cause a memory leak anyway. Even worse, there might be an obvious way of releasing ownership of the pointer other than release itself. Or another smart pointer class without an explicit constructor which could accidentally ingest the raw pointer obtained via get, leading to double frees...


Are there any loopholes? If there are, can they be fixed by adding some more simple restrictions? (not allocating any memory doesn't count!) And if a set of coding guidelines that prevents all types of memory errors can be reached, would it be okay to completely forget about the details of memory management?

like image 982
PBS Avatar asked Aug 13 '15 17:08

PBS


3 Answers

I thought cyclic references were only a problem with std::shared_ptr...

struct X
{
    std::unique_ptr<X> x;
};

void leak()
{
    auto x = std::make_unique<X>();
    x->x = std::move(x);
}

This can be fixed by ensuring that there is no cycle in the graph of types formed by adding an edge from A to B if and only if A contains a member std::unique_ptr<C> where C is a base of B.

like image 131
PBS Avatar answered Nov 14 '22 23:11

PBS


struct evil {
  std::shared_ptr<evil> p; // Alternatively unique_ptr
};
void foo() {
  auto e = std::make_shared<evil>(); // Alternatively make_unique
  e->p = e; // Alternatively std::move(e)
}
int main() {
  for (unsigned i = 1; i != 0; ++i) {
    foo();
    if (i % 100000000)
      std::cout << "I leak\n";
  }
}

the above program obeys your restrictions, and leaks like a sieve.

On top of that, undefined behavior can cause leaks.

like image 22
Yakk - Adam Nevraumont Avatar answered Nov 14 '22 21:11

Yakk - Adam Nevraumont


would it be okay to completely forget about the details of memory management?

I'd say the answer to this is going to be no in programming for the foreseeable future. Even in garbage collected languages today you can't forget about the details of memory management if you want a performant application.

Memory leaks still happen in garbage collected languages when programs accidentally hang onto references that are no longer needed. Following the rules you set out above for C++ would still be prone to the same issues and is even more likely to be an issue with uses of shared_ptr. Common errors of this type are hanging on to objects in a container or through observers for managed references in a garbage collected language or shared_ptr in C++.

like image 29
mattnewport Avatar answered Nov 14 '22 23:11

mattnewport