4

In Effective C++ 3/E, I read this:

This is exception unsafe code:

class Test { };
void foo(const std::shared_ptr<Test> &ptr, int i);
int bar();
...

foo(std::shared_ptr<Test>(new Test), bar());

Because compiler can implement like this:

  1. run new Test
  2. call bar() <-- if bar() throws exception, the object of Test allocated by new Test cannot be deleted.
  3. call constructor of std::shared_ptr<Test>
  4. call foo()

But in this case, compiler can know there's memory leak. Can't compiler do delete automatically if exception is thrown?

In addition, compiler does automatically delete in that case:

Test *p = new Test;

that is implemented like this:

  1. call operator new to allocate memory
  2. call constructor of Test. If constructor throws exception, memory is automatically deleted.

Why compiler doesn't do in first case, unlike second case?

ikh
  • 10,119
  • 1
  • 31
  • 70
  • This is why we don't create `shared_ptr` without `std::make_shared`. – Chnossos Apr 25 '14 at 10:52
  • @Chnossos I know, but I think there's no exception-safety problem even if I use constructor of `shared_ptr`. And there are some case that I cannot use `make_shared`. (e.g. creating shared_ptr of class whose constructor isn't public) – ikh Apr 25 '14 at 11:01
  • @ikh: In that case, `std::make_shared` can trivially be wrapped by a `TestFactory::make_shared` (or whatever class does have access to the ctor) – MSalters Apr 25 '14 at 12:11
  • 1
    In the second case, it is `operator new` that calls the constructor. Therefore, it is `operator new` that frees any memory it allocated, if the constructor throws. The compiler doesn't have to perform any magic. – D Drmmr Apr 25 '14 at 12:47
  • @MSalters I know, but I'm too lazy to do that.. – ikh Apr 25 '14 at 23:29

1 Answers1

4

The compiler cannot normally know that there is a memory leak. Once the code returns from the constructor of Test, the compiler must assume that the object has been fully and correctly constructed, which could (and often does) mean that the constructor has registered a pointer to it somewhere, and that other code will expect to find it.

The standard could have specified that an exception will cause delete to be called on all objects newed in the complete expression it passes through. There are a number of historical reasons why this wasn't even considered. And today, it would probably break too much existing code.

The standard also could have imposed a strict ordering on the evaluation of an expression. This would remove a lot of issues of undefined behavior, and make code more deterministic (so tests are more reliable). Historically, C didn't take this route because of its impact on optimization; C++ has kept the rule.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • You'd be effectively introducing special cases for new/delete that aren't there for fopen/fclose etcetera. Possible, but inconsistent. – MSalters Apr 25 '14 at 12:09
  • @MSalters Good point. About the only rule change I think could be considered would be imposing an order on the evaluation of expressions. And there's a lot of opposition to that one on the part of people who write optimizers. – James Kanze Apr 25 '14 at 12:28