Здравствуйте, alzt, Вы писали:
A>Здравствуйте, __kain, Вы писали:
A>Извиняюсь, что не разобрался в коде, бросилось в глаза следующее: A>
A>delete _msg;
A>
A>вместо A>
A>delete[] _msg;
A>
Да, все остальный упустили этот факт...
Но в данном случае причина не в этом, хотя, безусловно, это действительно ошибка. Насколько можно судить по коду — автор вопроса использует компилятор от MS, а он прощает использование delete вместо delete[] для встроенных типов.
Здравствуйте, __kain, Вы писали:
__>Вот и сбылся со мной анекдот — сообщение об ошибке вызвало ошибку __>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает.
Правило Большой Тройки:
Если ты реализуешь либо конструктор копирования, либо оператор присваивания, либо деструктор, то скорее всего тебе надо реализовать их все три.
Лучше это вшить себе в ПЗУ, если этого там ещё нет
Ух ты! И с какой версии VC позволяет вызывать конструктор из другого конструктора?
Дело в том, что в стандартном С++ так делать нельзя.
__>Я теперь понял, что происходит при передаче по ссылке: вызывается конструктор копирования
Это заблуждение. Дело не в передаче по ссылке, а в том, как именно создается объект — исключения. А создается он как копия объекта, указанного в throw (здесь
). Так что ссылка в catch совершенно не при чем.
__>Тогда понятно, почему глюк был. Я раньше думал, что если пишу "&", то "волшебным" образом передается в точности тот объект, который я ему передал! Как если бы я его использовал в месте вызова.
Это так и есть. В этом плане ссылка аналогична указателю.
__>wstring можно использовать, и boost, но мне было важно разобраться, в чем причина моей ошибки.
Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает. Помогите
Здравствуйте, Smal, Вы писали:
S>Здравствуйте, __kain, Вы писали:
__>>Добрый день!
__>>Вот и сбылся со мной анекдот — сообщение об ошибке вызвало ошибку
S>А конструктор копирования? (и оператор = тоже неплохо бы)
Здравствуйте, __kain, Вы писали:
__>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает. Помогите
Подумай на тему, какие члены класса генерируются компилятором по умолчанию и как именно они работают.
Здравствуйте, __kain, Вы писали:
__>В двух словах: генерирую исключение, хочу посмотреть текст. Вылетаю. Вот код: __>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает. Не могу понять, почему приведенный текст не работает.
Если нельзя использовать std::wstring, то можно так:
Я теперь понял, что происходит при передаче по ссылке: вызывается конструктор копирования Тогда понятно, почему глюк был. Я раньше думал, что если пишу "&", то "волшебным" образом передается в точности тот объект, который я ему передал! Как если бы я его использовал в месте вызова.
wstring можно использовать, и boost, но мне было важно разобраться, в чем причина моей ошибки.
ВСЕМ ОГРОМНОЕ СПАСИБО! Плюсы с меня
P.S.
Про delete[] я знаю, просто пробовал по-разному запустить, думал именно он влияет на ошибку.
Здравствуйте, Bell, Вы писали:
B>Ух ты! И с какой версии VC позволяет вызывать конструктор из другого конструктора? B>Дело в том, что в стандартном С++ так делать нельзя.
Всего лишь вариант реализации. Запускал на VC2005.
B>Это заблуждение. Дело не в передаче по ссылке, а в том, как именно создается объект — исключения. А создается он как копия объекта, указанного в throw (здесь
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, __kain, Вы писали:
__>>Причем получается так, что при переделке с использования wchar_t на std::wstring пример работает.
J>std::wstring в классе исключения — плохая идея, потому что его конструктор может бросить исключение
Ага, когда кончилась память . Но тут уж тебя врядли что-то спасет.
Здравствуйте, Smal, Вы писали:
J>>std::wstring в классе исключения — плохая идея, потому что его конструктор может бросить исключение
S>Ага, когда кончилась память . Но тут уж тебя врядли что-то спасет.
У тебя есть возможность корректно завершить приложение (хотя бы путем корректного проброса исключения на самый верх), чтобы отработали все деструкторы, сохранились все файлы и т.д.
А это всегда лучше, чем terminate.
Здравствуйте, jazzer, Вы писали:
S>>Ага, когда кончилась память . Но тут уж тебя врядли что-то спасет.
J>У тебя есть возможность корректно завершить приложение (хотя бы путем корректного проброса исключения на самый верх), чтобы отработали все деструкторы, сохранились все файлы и т.д. J>А это всегда лучше, чем terminate.
Кто как думает, как лучше реализовывать класс исключения в таком свете? Мне так чувствуется, что сильно хорошего решения тут вообще не придумать...
std::exception в msvc++ при нехватке памяти тупо выдаёт строку "Unknown exception".
Уважаемый г-н MaximE предпочитает (если более точно — предпочитал, но в прошедшем времени как-то плохо получается) делать следующим образом. В класс исключения добавляем встроенный буфер некого фиксированного размера, сообщение всегда храним в этом буфере. Пока стек не кончится мы в танке. В смысле не теряем пользовательское сообщение.
Так же разумным выглядит следующий подход. Разрешить такую форму вызова:
Т.к. сообщения часто бывают именно статическими. А если оно не статическое, то всё равно есть большая вероятность, что bad_alloc вылетит ещё при формировании сообщения.
Это приём тоже используется в std::exception в msvc++. Это даже доступно для пользователя: std::exception ex ("Some static message", 1);
Так же можно завести глобальный или на-поток пул памяти для сообщений исключений. И использовать его только, если свободная память исчерпалась. В таком случае есть большая вероятность, что пользовательское сообщение не пропадёт.
R>Кто что думает?
Можно так: вместо строки хранить в исключении id строки сообщения. Обработчик по id залезет в таблицу строк и покажет сообщение пользователю.
Минусы: на весь проект заводить единую таблицу строк, enum с id сообщений, строки только статические.
Можно так:
class BaseException
{
public:
virtual void printMessage(std::ostream& out) const;
};
class ExceptionA : public BaseException
{
int arg1_;
int arg2_;
public:
ExceptionA(int arg1, int arg2): arg1_(arg1), arg2_(arg2) {}
public:
virtual void printMessage(std::ostream& out) const
{
out << "My Message " << arg1_ << " " << arg2_;
}
};
class ExceptionB : public BaseException
{
int arg_;
public:
explicit ExceptionB(int arg): arg_(arg) {}
public:
virtual void printMessage(std::ostream& out) const
{
out << "Another Message " << arg_;
}
};
Минусы: Строки в аргументах конструктора исключения поломают всю красоту с не выделением памяти
Можно еще так:
class Excepton
{
const char* sourceName_;
int line_;
public:
Exception(const char* sourceName, int line): sourceName_(sourceName), line_(line) {}
void printMessage(std::ostream& out) const
{
out << sourceName_ << ":" << line_;
}
};
Т.к. под макросом __FILE__ компилятор прячет статическую переменную, то выделять память под Excepton::sourceName_ не нужно.
В конструкторе Exception можно немного "подрихтовать" sourceName: оставить только имя файла, полный путь отрезать.
Такое описание исключения получается достаточно информативным. Хотя таким образом сообщать пользователю о не найденном файле не лучший вариант
Еще вариант: все три (два с половиной, последние два сильно похожи один на другой) предложенных выше варианта скрестить
Здравствуйте, Bell, Вы писали:
__>>Я теперь понял, что происходит при передаче по ссылке: вызывается конструктор копирования B>Это заблуждение. Дело не в передаче по ссылке, а в том, как именно создается объект — исключения. А создается он как копия объекта, указанного в throw (здесь
Здравствуйте, Seal08, Вы писали:
S>Копия создаётся именно в catch, например если написать S>то никакой копии создаваться не будет и программа отработает нормально (только что проверил в gcc).
По стандарту, если я не ошибаюсь, копий объекта исключения может нагенериться сколько угодно независимо от способа ловли.
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Seal08, Вы писали:
S>>Копия создаётся именно в catch, например если написать S>>то никакой копии создаваться не будет и программа отработает нормально (только что проверил в gcc).
J>По стандарту, если я не ошибаюсь, копий объекта исключения может нагенериться сколько угодно независимо от способа ловли.
15.1/3
A throw-expression initializes a temporary object, called the exception object,
...
Как видишь, речь идет об одной копии. На количесво копий может повлиять как раз способ ловли — к примеру если catch ловит по значению...
Здравствуйте, Bell, Вы писали:
J>>По стандарту, если я не ошибаюсь, копий объекта исключения может нагенериться сколько угодно независимо от способа ловли. B>
B>15.1/3
B>A throw-expression initializes a temporary object, called the exception object,
B>...
B>Как видишь, речь идет об одной копии. На количесво копий может повлиять как раз способ ловли — к примеру если catch ловит по значению...
Может быть. Меня слово initializes смущает... В нем могут прятаться орды временных объектов...
Нету времени читать стандарт, да и неважно это.
Хотя, в принципе, если иметь гарантии, можно было бы в классе исключения держать std::auto_ptr на буфер со строкой ошибки — он будет корректно передавать владение и грохнет память в конце.
Но лично я бы завел просто статический буфер (в TLS, если много потоков) и писал бы все в него, а в классе исключения держал бы указатель на него, и все, никаких проблем с копированием и скоростью.
Здравствуйте, jazzer, Вы писали:
J>Но лично я бы завел просто статический буфер (в TLS, если много потоков) и писал бы все в него, а в классе исключения держал бы указатель на него, и все, никаких проблем с копированием и скоростью.
Хммм... А какого размера буфер? Особенно учитывая, что одновременно может быть несколько "активных" исключений...
J>У тебя есть возможность корректно завершить приложение (хотя бы путем корректного проброса исключения на самый верх), чтобы отработали все деструкторы, сохранились все файлы и т.д.
Маловероятно. Если уж у тебя память кончилась, то надеятся на то, что удастся посохранять файлы и позакрывать всякие ресурсы, не особо приходится.
Здравствуйте, Vamp, Вы писали:
J>>У тебя есть возможность корректно завершить приложение (хотя бы путем корректного проброса исключения на самый верх), чтобы отработали все деструкторы, сохранились все файлы и т.д.
V>Маловероятно. Если уж у тебя память кончилась, то надеятся на то, что удастся посохранять файлы и позакрывать всякие ресурсы, не особо приходится.
delete не аллокирует новую память, free() тоже, fclose() тоже, closesocket() тоже... вообще это хороший тон — делать функцию освобождения ресурса nofail...
ну если в лог не сможем чего записать — ну и фиг с ним.