Владеет ли std :: exception чем?

Я получаю свое собственное исключение, назовите это MyException, от std::system_error и переопределил what() рассчитать и вернуть мое сообщение. MyExceptionСписок инициализаторов не вызывает переопределение конструктора system_error, которое принимает сообщение.

Если я поймаю MyException и скопировать его в std::exception результат звонка what() на std::exception является nullptr, Это имеет смысл.

У меня вопрос, если я использую конструктор system_exception, который принимает сообщение при инициализации MyException, указано, что system_error возьмет копию сообщения и будет владельцем и освободит ее?

Я предполагаю, что это позволило бы std::exception копия MyException чтобы иметь возможность вернуть действительный what(), Хотя я бы оценил производительность в том, что «что» нужно вычислять каждый раз, когда новый MyExceptions создано; Я не могу лениво вычислить это только тогда, когда то, что () сначала вызывается.

Я немного обеспокоен владением строкой «что», как what() возвращает char* и не const std::string&,

Код выглядит примерно так (я не скомпилировал это):

    class MyException : public std::system_error
{
std::string what_;
public:
MyException(int errorValue, const std::error_category& category)
: std::system_error(errorValue, category)
{}

char* what() const
{
what_ = "MyException: " + to_string(code().value());
return what_.c_str();
}
};

int main()
{
std::exception ex;

try
{
throw MyException(4, system_category());
}
catch( const MyException& e )
{
ex = e;
}

printf("what= %s", ex.what());

return 1;
}

2

Решение

У меня вопрос, если я использую конструктор system_exception, который принимает сообщение при инициализации MyException, указано, что system_error возьмет копию сообщения и будет владельцем и освободит ее?

Да, это гарантировано стандартом.

Начать, std::exception не владеет whatstd::runtime_error делает. std::runtime_errorКонструкторы определены следующим образом ([runtime.error] p2-5):

runtime_error(const string& what_arg);

Последствия: Создает объект класса runtime_error,
Постусловие: strcmp(what(), what_arg.c_str()) == 0,

runtime_error(const char* what_arg);

Последствия: Создает объект класса runtime_error,
Постусловие: strcmp(what(), what_arg) == 0,

Таким образом, он должен хранить копия из what_arg внутренне, поскольку нет никаких требований относительно срока действия значения, переданного в.

Далее есть [исключение] р2:

Каждый стандартный класс библиотеки T что происходит от класса exception должен иметь общедоступный конструктор копирования и общедоступный оператор присваивания копии, которые не выходят за исключением. Эти функции-члены должны соответствовать следующему постусловию: если два объекта lhs а также rhs оба имеют динамический тип T а также lhs является копией rhs, затем strcmp(lhs.what(), rhs.what()) должен быть равен 0,

Таким образом, должен существовать конструктор копирования, он никогда не должен выдавать, и копии должны поддерживать одинаковое возвращаемое значение для what(), Аналогично для оператора копирования-назначения.

Собрав все это вместе, мы можем предположить, что std::runtime_error должен сохранить значение, которое вы передаете what_arg внутренне в строке с подсчетом ссылок (чтобы избежать исключений из выделений при копировании), и значение будет сохраняться независимо от копирования и / или нарезки — но только до std::runtime_error, не до std::exception! (Более подробная информация об обоснованиях и требованиях, касающихся whatВ этом очень интересном ответе можно найти хранилище @HowardHinnant: переместить конструктор для std :: runtime_error)

std::system_error наследуется от std::runtime_errorТаким образом, все то же самое относится и к нему, и к любому производному от него типу (при условии, что производный тип поддерживает инвариант конструктора копирования без отбрасывания).

Я предполагаю, что это позволило бы std::exception копия MyException чтобы иметь возможность вернуть действительный what(),

Нет! Когда вы делаете std::exception копия из MyException, ты нарезка объект до менее производного типа, чем где whatЗначение физически сохраняется. если ты должен сделайте копию вашего исключения, наименее производный тип, который вы можете использовать std::runtime_error, (Вы всегда можете безопасно сделать std::exception ссылка к MyExceptionКонечно.) Другими словами, это никогда можно получить значимую строку из std::exception объект«s what(),


Этот код имеет поведение, которое вы хотите, переносимо:

#include <cstdio>
#include <stdexcept>
#include <system_error>
#include <string>

class MyException : public std::system_error {
public:
MyException(int errorValue, std::error_category const& category)
: std::system_error(
errorValue, category,
"MyException: " + std::to_string(errorValue)
)
{ }
};

int main() {
std::runtime_error ex;

try {
throw MyException(4, system_category());
} catch(MyException const& e) {
ex = e;
}

std::printf("what= %s", ex.what());
}

Я бы сказал, что плохо писать конструктор исключений, который выделяет (по понятным причинам), но, учитывая, что каждая текущая реализация стандартной библиотеки использует оптимизация коротких строк за std::basic_string<>на практике это крайне маловероятно.

6

Другие решения

Ваш вопрос связан с пониманием жизненного цикла исключения. Этот вопрос обсуждается в постах Вот а также Вот и может быть полезным.

Вы можете гарантировать, что срок действия вашего исключения будет продлен с помощью умного указателя. Я не уверен, как это повлияет на производительность, но вы, вероятно, могли бы использовать это для привязки к своему собственному расширению std::system_error и избегать копирования конструкции в целом. (На самом деле, я не гарантирую, что будет предотвращено создание копии. Создание умного указателя может копировать исключение, а может и не копировать, кажется. Но это скопировало бы ваше исключение, что должно быть правильным, если вы предоставите конструктор копирования что вы должны предоставить.) Ваша основная функция в конечном итоге будет выглядеть примерно так.

#include <exception> // std::exception_ptr

int main()
{
std::exception_ptr p;

try
{
throw MyException(4, system_category());
}
catch( const MyException& e )
{
p = std::current_exception();
}

try
{
std::rethrow_exception(p);
}
catch (const std::exception& e)
{
printf("what= %s", e.what());
}

return 1;
}

Это просто переписать пример использования указателя исключения, о котором я читал на cplusplus.com Вот, но я использовал ваш класс исключения, а не стандартное исключение, такое как std::logic_error,

Что касается вашего первоначального вопроса, кажется, что трудно сделать жесткие гарантии. Я нашел следующее утверждение в документации по оператору присваивания за исключением применительно к C ++ 11. В C ++ 98 даже эта гарантия не предоставляется.

Каждое исключение в стандартной библиотеке C ++ (включая это) имеет, по крайней мере, перегрузку оператора присваивания копии, которая сохраняет строковое представление, возвращаемое членом, как в случае совпадения динамических типов.

Тем не менее, динамический тип std::system_error не будет соответствовать динамическому типу std::exception в вашем случае, так что я не думаю, что это гарантированно сработает.

1

Исключению класса не принадлежит ни одна строка. Когда вы разрезаете свой объект исключения, вы получаете базовый объект исключения, который не имеет переопределенной виртуальной функции what ().

Волшебство функции what () заключается в виртуальной функции what () и в вашем производном классе. Вы можете передать const char *, хранящийся в статической памяти, объекту исключения, и он не будет скопирован.

Обратите внимание, что копия объектов при поднятии и исключении может вызывать новые исключения, и она не рекомендуется (например, после bad_alloc возможно, что вы не можете создать новый строковый объект). Вот почему исключения лучше отлавливать по ссылке, а не по значению.

0
По вопросам рекламы [email protected]