Остановить выполнение, не пропуская деструкторы

Можно ли завершить выполнение программы, не пропуская вызовы деструкторов? Например, в приведенном ниже коде деструктор для test никогда не будет вызываться из-за оператора exit(1).

#include <iostream>
#include <cstdlib>
using namespace std;

class A{
public:
    A(){cout << "Constructed.\n";}
    ~A(){cout << "Destroyed.\n";}
};

void func()
{
    //Assuming something went wrong:
    exit(1);  
}

int main(int argc, char *argv[])
{
    A test;
    func();
    return 0;
}

Что мне нужно, так это способ завершить программу (изнутри func()), которая вызывает все необходимые деструкторы перед завершением. До сих пор я обрабатывал это через возвращаемое значение func(), например:

bool func()
{
    //Assuming something went wrong:
    return false;
}

int main(int argc, char *argv[])
{
    A test;
    if( !func() )return 1;
    return 0;
}

Проблема с этим методом заключается в том, что он быстро становится очень раздражающим (и раздувает код), когда вам нужно применить его к ряду вложенных функций.

Есть ли способ добиться тех же результатов, что и во втором примере (правильные вызовы деструктора) с синтаксисом, аналогичным первому примеру (вызов exit(1), где бы вы ни находились)?


person Malabarba    schedule 29.11.2011    source источник
comment
Вы рассматривали возможность использования исключений? если вы вызовете исключение в функции, будут вызваны деструкторы.   -  person Nerdtron    schedule 29.11.2011
comment
Не используйте exit; скорее сгенерируйте исключение и перехватите его на уровне main.   -  person Kerrek SB    schedule 29.11.2011
comment
Это основная цель исключений (разрешить выполнение деструкторов).   -  person Steven Lu    schedule 29.11.2011
comment
@Nerdtron: Небольшое исправление: если вы сгенерируете и перехватите исключение, будут вызваны деструкторы. Если не ловится, то не уточняется, вызываются ли.   -  person Mike Seymour    schedule 29.11.2011


Ответы (4)


Выбросить исключение, поймать его в main и вернуться.

Это зависит от того, что ничто другое не поймает ваше исключение, не выбрасывая его повторно.

person JoeG    schedule 29.11.2011
comment
Важное замечание о том, что другие поймали ваше исключение. Если вы хотите быть деспотичным, вы можете выдать какой-либо тип, отличный от исключения, что-то, что вряд ли будет обработано другим кодом. Кто-нибудь знает, можно ли кинуть nullptr? - person Michael Price; 29.11.2011
comment
О, и, конечно же, вы не можете избежать ужасного синтаксиса catch(...). - person Michael Price; 29.11.2011
comment
@Майкл Прайс: Вы можете сделать свой собственный тип и бросить его. catch(...) все еще может быть проблемой. Будем надеяться, что любой код, делающий это, просто регистрирует и повторно выдает одно и то же исключение. - person Fred Larson; 29.11.2011
comment
@Michael: лучше всего просто определить класс, назовите его systemexit, который принимает желаемый код выхода в качестве параметра конструктора. Бросьте это. Если у него нет std::exception в качестве базового класса, то единственные способы, которыми кто-либо может его поймать, это: (а) явно поймать systemexit, и в этом случае они знают, что это значит, поэтому будем надеяться, что они делают это по уважительной причине и перебросит его; (b) catch(...), и в этом случае давайте надеяться, что они делают это по уважительной причине и либо повторно сгенерируют, либо, в худшем случае, прекратят. Другой риск заключается в том, что кто-то вызывает func из функции noexcept(true): результатом является завершение. - person Steve Jessop; 29.11.2011
comment
@SteveJessop - даже лучше, поскольку затем вы можете перегрузить оператор () для systemexit, а в своем блоке catch вы можете сказать myexit(); (где myexit — это имя объекта, который вы поймали). Вы можете использовать это для вызова деструкторов для любых глобальных переменных, которые не будут вызываться, поскольку они не затрагиваются раскручиванием стека. - person Michael Price; 29.11.2011
comment
@Майкл Прайс: Но main() может вернуться, что уничтожит любые глобальные переменные. Если есть глобальные указатели (содрогание), можно было бы надеяться, что в конце main() есть код, чтобы справиться с этим в любом случае. - person Fred Larson; 29.11.2011
comment
Очищаются ли глобальные переменные при вызове exit(). Я думаю, есть смысл, что они должны. - person Michael Price; 29.11.2011
comment
Я на самом деле проверил это, прежде чем спрашивать, и это не сработало. Оказывается, один из моих указателей вызывал неопределенное поведение :-$. - person Malabarba; 29.11.2011

Вы можете положиться на раскрутку стека для этого : когда вы хотите выйти, сгенерируйте исключение и перехватите его в main().

person NPE    schedule 29.11.2011

Есть несколько способов сделать это чисто.

Одним из решений является использование функции atexit, которая просто вызывает заданный указатель функции, когда программа завершается.

Вам нужно будет выделить все свои объекты из кучи, поддерживать некоторую глобальную таблицу с указателями на все экземпляры экземпляров класса, а затем просто перебирать таблицу delete для каждого экземпляра в зарегистрированной функции.

person Max DeLiso    schedule 29.11.2011

person    schedule
comment
Как было указано в комментариях к другому ответу, глобальные переменные будут очищены, просто вернувшись из main или вызвав выход, поэтому я не уверен, что в итоге даст вам перегрузка моего оператора. Возможно, я придумаю для него другое применение. - person Michael Price; 29.11.2011