0

The following code makes it so that a destructor is called twice.

#include <iostream>
#include <memory>
#include <exception>
#include <cstdlib> 

void myterminate()
{
    std::cout << "terminate\n";
    abort();
}

class data 
{
    int a;
public:
    data(int a) : a(a) { std::cout << "ctor " << a << "\n"; }
    ~data() { std::cout << "dtor " << a << "\n"; }
    static data failure(int a) { return data(a); }
};

void main()
{
    std::set_terminate(myterminate); //terminate is not called
    try
    {
        std::unique_ptr<data> u;
        u.reset(&data::failure(1));
        std::cout << "no worries\n"; //this prints 
        //destructor called at try-block end and attempt to destruct an invalid memory block.
    }
    catch (...)
    {
        std::cout << "caught\n"; //this can not catch the error
    }
    std::cout << "end\n"; //program crash, will not be called
}

How would I catch an error like this in production?

On a Release build the program crashes. On a Debug build it the on my system is: enter image description here

Patryk
  • 22,602
  • 44
  • 128
  • 244
Johannes
  • 6,490
  • 10
  • 59
  • 108

3 Answers3

4

How would I catch an error like this in production?

You cannot. The standard says that invalid memory access has undefined behaviour. There is no standard way to "catch" UB. Catching and terminate handler are for exceptions, which are defined behaviour.

What you could do, is use the debug build in production, and run it with valgrind or similar tool, so that you can at least analyze the error.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

"try/catch" such mistakes will not catch, because in this case a great probability of a crash. But a moment of destruction you can try to catch, using signals and more menne dignified exit the program. Looking away: #include , signal(), sig_atomic_t ...

Max ZS
  • 175
  • 11
0

First as noted in the comments you declare main as void main where the standard only allows two forms in § 3.6.1

  1. An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a declared return type of type int, but otherwise its type is implementation-defined. An implementation shall allow both

    (2.1) — a function of () returning int and

    (2.2) — a function of (int, pointer to pointer to char) returning int

Secondly you are resetting your unique_ptr to manage a temporary which when unique_ptr gets destroyed at the end of its scope will be deleted which results in undefined behavior. You can't predict/catch errors resulting from undefined behavior.

What you should do (if you really want to use dynamically allocated memory) you can return a pointer to object on the heap:

#include <iostream>
#include <memory>
#include <exception>
#include <cstdlib> 

void myterminate()
{
    std::cout << "terminate\n";
    abort();
}

class data 
{
    int a;
public:
    data(int a) : a(a) { std::cout << "ctor " << a << "\n"; }
    ~data() { std::cout << "dtor " << a << "\n"; }
    static data* failure(int a) { return new data(a); }
};

int main()
{
    std::set_terminate(myterminate); //terminate is not called
    try
    {
        std::unique_ptr<data> u;
        u.reset(data::failure(1));
        std::cout << "no worries\n"; //this prints 
        //destructor called at try-block end and attempt to destruct an invalid memory block.
    }
    catch (...)
    {
        std::cout << "caught\n"; //this can not catch the error
    }
    std::cout << "end\n"; //program crash, will not be called
}

Online code: http://melpon.org/wandbox/permlink/pdiijgDxBshVOYRu

Patryk
  • 22,602
  • 44
  • 128
  • 244