54

I found that there are three ways to catch an exception, what are the differences?

1) catch by value;

2) catch by reference;

3) catch by pointer;

I only know that catch by value will invoke two copies of the object, catch by reference will invoke one. So how about catch by pointer? When to use catch by pointer? In addition to throw an object, can I throw a pointer to an object like this?

class A {}

void f() {

  A *p = new A();
        throw p;


}
Gregory Pakosz
  • 69,011
  • 20
  • 139
  • 164
skydoor
  • 25,218
  • 52
  • 147
  • 201
  • 7
    You can __not__ catch an exception by pointer. You can catch an exception that happens to be a pointer. The problem is that A and A* are two completely different types. If you throw a pointer to A then you can only catch value or reference. But it is a A* that you are catching by value or reference not the A. – Martin York Jan 07 '10 at 19:45
  • 1
    Also, since C++ compilers are permitted (though, not obligated) to elide the both copying processes, and as for the first of the copying (then throwing), if does not elide the copying then must to move the object instead of to copy. – Arthur P. Golubev Aug 24 '21 at 23:56

5 Answers5

89

The recommended way is to throw by value and catch by reference.

Your example code throws a pointer, which is a bad idea since you would have to manage memory at the catch site.

If you really feel you should throw a pointer, use a smart pointer such as shared_ptr.

Anyway, Herb Sutter and Alexei Alexandrescu explain that really well in their C++ Coding Standards book which I paraphrased.

See C++ Coding Standards: Throw by Value, Catch by Reference.

Gregory Pakosz
  • 69,011
  • 20
  • 139
  • 164
  • 16
    And if the reason you are throwing is because you are out of memory, then trying to allocating a new object to throw is not going to help. – Nathan Osman Jan 07 '10 at 19:37
  • 3
    You'd either throw a pointer to A, or you'd throw std::bad_alloc, according to whether the A could be allocated. So at least you'd throw something... – Steve Jessop Jan 07 '10 at 20:50
  • I have seen code like `const std::runtime_error err; throw err;` am I correct assuming that this will not in fact be caught by reference (no conversion from `const std::runtime_error&` to `std::runtime_error&`)? This will therefore be caught by the runtime and presumably crash the program. Am I right? – the swine Oct 08 '14 at 08:57
  • @SteveJessop That's a bright observation actually. I always worried about the `std::string` with exception description being copied in `std::exception` constructor. But if it fails to copy, I guess a reason-less `std::bad_alloc` is thrown anyway. Or maybe infinite recursion ensues :). Nicely thought out. – the swine Oct 08 '14 at 09:07
  • 1
    @theswine No. When throwing a temporary object is made. [N3337, 15.1$3] Memory for it is allocated in unspecified way but not by operator new. [N3337, 151.$4] Whatevery you have in the throw expression is used to initialize that temporary object (either by copy - possibly elided - or by move). In your particular example a temporary object of std::runtime_error will be made and initialized by a copy from err. And then could be caught by both & and const&. – Adam Badura Nov 25 '14 at 20:37
18

Catch follows normal assignment compatibility rules, that is, if you throw a value, you can catch it as value or reference, but not as pointer; if you throw a pointer, you can catch it only as a pointer (or reference to a pointer...).

But it doesn't really make sense to throw pointers, it will only cause memory management headaches. Thus, you should, in general follow the rule throw by value, catch by reference, as explained by Gregory.

oefe
  • 19,298
  • 7
  • 47
  • 66
  • I apologise if I come off as naive, but if we **catch** by _reference_, won't that be UB? Because the scope of that object that got created in the **try** block, is destroyed right? Or is it the case that, it allocates the return object dynamically, and then sends a reference to that? – Floatoss Jul 09 '23 at 12:44
6

Microsoft's MFC uses catch by pointer, but I think that was for compatibility with the compiler before try and catch were properly implemented; originally they used TRY and CATCH macros to simulate it. Each exception derives from CException, which has a method to determine whether the object needs to be deleted.

I wouldn't recommend that for any modern exception design. Catch by reference is the way to go.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
3

While it's possible to throw essentially any object of any type, there's little (if anything) to be gained by doing this. Dynamic allocation is useful primarily when an object needs to have a lifetime doesn't fit with automatic allocation -- i.e. you want its lifetime to be independent of normal program scope.

In the case of an exception object, however, that doesn't really make much sense. An exception object is normally only used inside of an exception handler, and you clearly want it to be destroyed when you exit the (last) handler for that exception.

There's also the fact that you generally want to keep exception handling code fairly simple. Just for example, if you're trying to report the free store/heap being exhausted or corrupt, trying to allocate your exception object off that exhausted/corrupt free store/heap usually won't work very well...

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

There isn't really a good scenario for catching/throwing an exception by pointer. C++ semantics allow it, but it's not terribly useful, as most of the time you'll be throwing a temporary exception or string object.

However, some libraries (Boost.Graph does this, I believe) use throw to pass a return value back to the caller from a deeply recursed function; in a situation like this, the return value may be a pointer, so throwing a pointer would make sense.

Collin Dauphinee
  • 13,664
  • 1
  • 40
  • 71