Лучший способ иметь дело "локально" с исключениями, брошенными в конструктор объекта - PullRequest
0 голосов
/ 22 марта 2020

Я уже некоторое время думаю об этом вопросе, но пока не нашел в Интернете достаточно удовлетворительного ответа. Итак, я здесь.

Допущения

Для ясности ограничимся C ++ 11 и пользовательским объектом. Под пользовательским объектом я подразумеваю пользовательский класс, который полностью контролируется разработчиком. Все приведенные ниже фрагменты кода не предназначены для того, чтобы быть компилируемыми или даже синтаксически правильными. Они просто иллюстрируют концепцию.

Граничные условия

Наш объект имеет нетривиальный конструктор, который может генерировать исключения в случае возникновения ошибки. При создании этого объекта я бы хотел поймать и обработать исключения как можно ближе к точке создания объекта , чтобы сделать код более читабельным и перехватывать только исключения конструктора и ничего больше.

Пример 1

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

class MyClass {
public:
  MyClass() {
    throw 1;
  }
}

int main() {

  try {

    MyClass my_obj;

    try {
      // Do something with my_obj that may throw
    } catch (...) {
      // deal with exceptions
    }

  } catch(...) {
    // deal with constructor exceptions
  }
}

Пример 2

Здесь я использую std :: unique_ptr для разделения объявления объекта и инициализации. Недостатком является то, что теперь я создаю объект в куче, а не в стеке, даже если у меня нет веских причин для этого.

class MyClass {
public:
  MyClass() {
    throw 1;
  }
}

int main() {

  std::unique_ptr<MyClass> my_obj_ptr;

  try {
    my_obj_ptr = boost::make_unique<MyClass>();  
  } catch (...) {
    // deal with constructor exceptions
  }

  // continue to use my_obj
}

Пример 3

Измените внутреннее состояние объекта и проверьте, что.

class MyClass {
private:
  good_internal_state;

public:
  MyClass() : good_internal_state(true) {
    try {
      throw 1;
    } catch(...) {
       good_internal_state = false;
    }
  }

  bool IsInternalStateGood() {
    return good_internal_state;
  }
}

int main() {

  MyClass my_obj;
  if (!my_obj.IsInternalStateGood()) {
    // Do something
  }

  // continue to use my_obj
}

Сейчас я склоняюсь к случаю примера 2, но мне хотелось бы знать, какой синтаксически правильный способ выполнения sh что Я хочу.

1 Ответ

1 голос
/ 22 марта 2020

Я бы не сказал, что любая из этих версий более или менее верна (за исключением некоторых опечаток). Пример 2 выглядел как тот, который я бы использовал. Здесь, похоже, самая большая проблема. Переменная должна быть объявлена ​​вне try, но должна быть инициализирована внутри. Если вам не нравится использовать кучу std::optional, это может быть полезно:

int main() {

  std::optional<MyClass> maybeMyClass;

  try {
    maybeMyClass.emplace(/*constructor parameters*/);
  } catch (...) {
    // deal with constructor exceptions
  }

  // continue to use my_obj
  maybeMyClass->foo();
}

Несмотря на то, что синтаксис может подразумевать иное, значение, управляемое std::optional, выделяется как след этого std::optional (в данном случае в стеке).


Вы также можете использовать фабричную функцию createMyClass, которая будет возвращать std::optional, для этого потребуется MyClass, чтобы иметь конструктор перемещения, который не должен быть дорогим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...