Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Исключение нужно бросить в конструкторе.


Автор: JackYF 9.1.2008, 19:13
Собственно, сабж. По текущей архитектуре кода исключение должно броситься в конструкторе объекта, если инициализация прошла неуспешно. Код ошибки вернуть не могу - конструктор всё-таки smile.

А что вы делаете в таких случаях?

Автор: marcusmae 9.1.2008, 19:37
JackYF, замечательный опрос!

Я ответил, что стараюсь не бросать исключений в конструкторах, но иногда приходится. А вообще, думаю, что у меня на этот счёт пробел в теоретических познаниях : ума не приложу, что ж ещё делать, если конструктору скормили, скажем, недопустимое значение аргумента. Хуже того, это какой-нить десятый сверху базовый конструктор, который вызывается по ":" из конструктора наследуемого класса... Интересно, какие будут идеи по поводу последнего варианта ответа.

Автор: archimed7592 9.1.2008, 19:44
Ответил 1-й вариант(надо бросить - бросаю), но есть у меня маленькое уточнение smile.
"Надо" - понятие растяжимое. Скажем так, если можно без крови перепроектировать несколько классов, чтобы бросать исключение не пришлось, то я именно так и сделаю. Другими словами - не всегда "надо", даже если кажется, что "просто необходимо" smile.

Автор: nickless 9.1.2008, 19:59
ИМХО если пользуешься исключениями, так пользуйся, а как раз в конструкторе удобно, не надо ничего изобретать.

Автор: Earnest 9.1.2008, 20:24
Я выбрала первый вариант... Строго говоря, что в конструкторе исключения бросать, что в других функция - лишь бы не в деструкторе.
Однако на практике я делаю все же немного по-другому, особенно если речь идет о глобально используемых классах-объектах. Такой конструктор (который может привести к исключению) я делаю приватным и заворачиваю в фабричную функцию (обычно статик этого же класса). И она возвращает уже указатель или 0, обрабатывая исключения или просто проверяя условия внутри себя. 
Решение зависит от контекста - ведь исключения исключениям рознь. Неправильный параметр - если только его не пользователь ввел (или он откуда-то извне появился) - это должно быть выковырено на этапе отладки - здесь лучше исключения и\или ASSERT's.
А вот несуществующий файл или отсутствие ресурса или еще что-то в этом роде - другое дело. При этом, если обработка неудачного создания всегда одинакова, фабричная функция имеет преимущества - пишешь обработку один раз, а потом только if 0 return - несколько короче, чем try\catch, да еще если неоднократно...

Автор: JackYF 9.1.2008, 20:40
Цитата(Earnest @  9.1.2008,  19:24 Найти цитируемый пост)
Строго говоря, что в конструкторе исключения бросать, что в других функция - лишь бы не в деструкторе.

Разве? ЕМНИП, контейнеры STL по этому поводу не гарантируют уже не помню что, если в конструкторе может выброситься исключение.
Скажем, если я создаю вектор из 10 элементов, и 5-й конструктор выбросил исключение - то в каком состоянии останется контейнер?

Автор: archimed7592 9.1.2008, 20:44
Цитата(JackYF @  9.1.2008,  20:40 Найти цитируемый пост)
то в каком состоянии останется контейнер? 

Его конструктор выкинет исключение(как new T[]).

Автор: JackYF 9.1.2008, 20:53
Цитата(archimed7592 @  9.1.2008,  19:44 Найти цитируемый пост)
Его конструктор выкинет исключение(как new T[]). 

отлично, а кто будет освобождать память из-под 4 элементов, конструкторы которых уже вызвались?

Автор: archimed7592 9.1.2008, 21:45
Цитата(JackYF @  9.1.2008,  20:53 Найти цитируемый пост)
отлично, а кто будет освобождать память из-под 4 элементов, конструкторы которых уже вызвались? 

Ну, во-первых, память выделяется под всё сразу. А, во-вторых, как и в случае new T[], вызовутся деструкторы уже сконструированных элементов в порядке, обратном конструированию, потом освободится память(та что под всё сразу) и, наконец, исключение будет переброшено.

Автор: bsa 9.1.2008, 21:52
Цитата(archimed7592 @ 9.1.2008,  21:45)
Ну, во-первых, память выделяется под всё сразу. А, во-вторых, как и в случае new T[], вызовутся деструкторы уже сконструированных элементов в порядке, обратном конструированию, потом освободится память(та что под всё сразу) и, наконец, исключение будет переброшено.

Код
#include <iostream>
#include <exception>
#include <vector>
#include <string>

struct Error : public std::exception
{
        Error(const std::string &text) throw() : m_text(text) {}
        ~Error() throw(){}
        const char* what() const throw() {
                return m_text.c_str();
        }
private:
        std::string m_text;
};

class Test
{
        static int counter;
        int cnt;
public:
        Test() {
                cnt = counter;
                if (--counter)
                        throw Error("Error");
        }
        ~Test() {
                std::cout << "Cnt=" << cnt << " destroyed" << std::endl;
                ++counter;
        }
};

int Test::counter = 5;

int main()
{
        try {
                std::vector<Test> test(10);
        } catch(std::exception &e) {
                std::cerr << "Exception: " << e.what() << std::endl;
        }
        return 0;
}
Вывод
Код
$ ./a.out 
Exception: Error
gcc версия 4.1.2 (Gentoo 4.1.2)
Я что-то делаю нетак?

Автор: JackYF 9.1.2008, 22:26
Цитата(archimed7592 @  9.1.2008,  20:45 Найти цитируемый пост)
уже сконструированных элементов в порядке, обратном конструированию

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

Добавлено через 57 секунд
о, bsa, отлично, уже живой пример есть smile

Автор: archimed7592 9.1.2008, 22:28
Цитата(bsa @  9.1.2008,  21:52 Найти цитируемый пост)
Я что-то делаю нетак? 

Угу. Замени if (--counter) на if (!--counter).

Добавлено через 1 минуту и 12 секунд
Цитата(JackYF @  9.1.2008,  22:26 Найти цитируемый пост)
в том-то и дело, что могут не вызваться - конструктор не отработал - мы не имеем право вызывать деструктор.

Так, давай, с чувством, с толком, с расстановкой - чей конструктор не отработал, чей деструктор не имеем право вызвать и т.д. smile.

Автор: nickless 9.1.2008, 22:56
Вот http://www.research.att.com/~bs/3rd_safe.pdf от Страуструпа [pdf, 200kB] как дополнение к его же http://www.research.att.com/~bs/bs_faq2.html#ctor-exceptionsу

Автор: bsa 9.1.2008, 23:41
Цитата(archimed7592 @ 9.1.2008,  22:28)
Цитата(bsa @  9.1.2008,  21:52 Найти цитируемый пост)
Я что-то делаю нетак? 

Угу. Замени if (--counter) на if (!--counter).

 smile 
Там не только это нужно было исправить.
Код
#include <iostream>
#include <exception>
#include <vector>
#include <string>

struct Error : public std::exception
{
        Error(const std::string &text) throw() : m_text(text) {}
        ~Error() throw(){}
        const char* what() const throw() {
                return m_text.c_str();
        }
private:
        std::string m_text;
};

class Test
{
        static int counter;
        int cnt;
public:
        Test() {
                cnt = counter--;
                if (!counter)
                        throw Error("Error");
                std::cout << "Cnt=" << cnt << " created" << std::endl;
        }
        Test(const Test&) {
                cnt = counter--;
                if (!counter)
                        throw Error("Error");
                std::cout << "Cnt=" << cnt << " created" << std::endl;
        }
        ~Test() {
                std::cout << "Cnt=" << cnt << " destroyed" << std::endl;
                ++counter;
        }
};

int Test::counter = 5;

int main()
{
        try {
                std::vector<Test> test(10);
        } catch(std::exception &e) {
                std::cerr << "Exception: " << e.what() << std::endl;
        }
        return 0;
}
Зато вывод стал таким:
Код
$ ./a.out 
Cnt=5 created
Cnt=4 created
Cnt=3 created
Cnt=2 created
Cnt=4 destroyed
Cnt=3 destroyed
Cnt=2 destroyed
Cnt=5 destroyed
Exception: Error

Автор: JackYF 9.1.2008, 23:47
Цитата(archimed7592 @  9.1.2008,  21:28 Найти цитируемый пост)
чей деструктор не имеем право вызвать

итак, пример.
пускай есть класс с двумя динамическими полями. Пускай в конструкторе под них выделяется память. Что будет, если я выброшу исключение после того, как память под первое поле уже выделил, а под второе ещё нет?
1) деструктор вызовется. При попытке освободить память из-под второго поля (там мусор, так как конструктор ничего не успел с ним сделать) - сегфолт.
2) деструктор не вызовется. Память, выделенная под первое поле, останется витать в воздухе.

Ещё варианты?

Автор: archimed7592 10.1.2008, 00:08
Цитата(JackYF @  9.1.2008,  23:47 Найти цитируемый пост)
Ещё варианты? 

Женя, ты сейчас жутко тупишь - завтра утром будешь долго смеятся smile.

Код

void stupid_func()
{
   void *p1 = operator new(100);
   if (std::rand() % 2)
      throw 0;
   void *p2 = operator new(200);
   // ...
   operator delete(p1);
   operator delete(p2);
}

void smart_func()
{
   std::auto_ptr< T > p1(new T());
   if (std::rand() % 2)
      throw 0;
   std::auto_ptr< T > p2(new T());
   // ...
}

Отгадай к какой из двух ф-ции относится предложенный тобою конструктор smile.
Аналогично ф-циям работают и конструкторы. Если он завершится исключением, то деструктор вызван не будет, но, будут вызваны деструкторы уже сконструированных полей и деструкторы базовых классов.

Почитай что-нибудь на тему базовой/строгой гарантии бессбойности в случае исключения smile.

Автор: JackYF 10.1.2008, 00:32
Цитата(archimed7592 @  9.1.2008,  23:08 Найти цитируемый пост)
Отгадай к какой из двух ф-ции относится предложенный тобою конструктор

вообще говоря, ни к одной. Итак, мой пример:
Код

#include <iostream>

class A
{
 public:
    ~A()
    {
        std::cout << "I'm destructor of A\n";
    }
};


class B
{
 public:
    ~B()
    {
        std::cout << "I'm destructor of B\n";
    }
};

class S
{
 private:
     A* pa;
     B* pb;
 public:
    S()
    {
         pa = new A;
         throw 0;
         pb = new B;
    }
    ~S()
    {
        delete pa;
        delete pb;
    }
};

int main()
{
    try
    {
        S* ps = new S;
        delete ps;
    }
    catch(...)
    {}
}


Почему не отработал деструктор класса A?

Автор: archimed7592 10.1.2008, 00:40
Цитата(JackYF @  10.1.2008,  00:32 Найти цитируемый пост)
вообще говоря, ни к одной

Если дописать rand, то очень смахивает на первую ф-цию smile.


Цитата(JackYF @  10.1.2008,  00:32 Найти цитируемый пост)
Почему не отработал деструктор класса A?

А почему он должен отрабатывать? Деструктор вызывается для сконструированных объектов. Для недоконструированных деструктор не вызывается. Т.е. не то, что до delete pa не доходит - до самого деструктора не доходит.
А вот немного модифицированный пример что выведет? smile

Код

struct E
{
  E()
  { throw 0; }
};

struct S
{
  A a;
  E e;
  B b;
};

int main()
{
   try { S s; } catch(...) { ; }
}


Добавлено через 3 минуты и 21 секунду
Кстати, учитывая поздний час и теоритическую сонность собеседников ещё раз упомяну:
Цитата(archimed7592 @  10.1.2008,  00:08 Найти цитируемый пост)
Аналогично ф-циям работают и конструкторы. Если он завершится исключением, то деструктор вызван не будет, но, будут вызваны деструкторы уже сконструированных полей и деструкторы базовых классов.


Автор: bsa 10.1.2008, 00:45
Цитата(JackYF @ 10.1.2008,  00:32)
Почему не отработал деструктор класса A?

ответ на этот вопрос ты и сам знаешь.
Вот archimed7592 предложил интересный вариант с использованием auto_ptr. В этом случае проблем быть уже не должно:
Код
class S
{
 private:
     A* pa;
     B* pb;
 public:
    S()
    {
         std::auto_ptr<A> apa(pa = new A);
         throw 0;
         std::auto_ptr<B> apb(pb = new B);
         apa.release();
         apb.release();
    }
    ~S()
    {
        delete pa;
        delete pb;
    }
};

Автор: Fazil6 10.1.2008, 01:06
Цитата(JackYF @  9.1.2008,  23:32 Найти цитируемый пост)
Почему не отработал деструктор класса A?
 ну естественно к динамически создаваемым объектам это не относится. Юзаем shared_ptr и проблема решена

Автор: JackYF 10.1.2008, 01:06
Цитата(bsa @  9.1.2008,  23:45 Найти цитируемый пост)
ответ на этот вопрос ты и сам знаешь.

Да, я задал его archimed7592у. Имелось в виду, что не всё так хорошо.

Отличия (семантико-логические) конструктора от любой другой обычной фукнции в том, что конструктор может работать с динамическими объектами напрямую, так как он "знает", что всегда есть (при наличии нормального программиста smile) деструктор, который корректно освободит память из-под динамических объектов.
Функция же должна надеяться только сама на себя - пары, которая вызовется к ней, нет, поэтому должна по возможности содержать объекты локальной области видимости, для которых в случае исключения вызовется деструктор. Только и всего.

Цитата(bsa @  9.1.2008,  23:45 Найти цитируемый пост)
В этом случае проблем быть уже не должно:

Да, неплохо. Хотя присутствует небольшой оверхед - в дополнении к вызовам двум операторов new и двум присваиваниям, мы имеем вызов двух конструкторов, двух деструкторов, двух функций. Впрочем, это должны быть мелочи...

Пошёл-ка я и вправду спать...

Автор: archimed7592 10.1.2008, 01:22
Цитата(JackYF @  10.1.2008,  01:06 Найти цитируемый пост)
Отличия (семантико-логические) конструктора от любой другой обычной фукнции в том, что конструктор может работать с динамическими объектами напрямую, так как он "знает", что всегда есть (при наличии нормального программиста smile) деструктор, который корректно освободит память из-под динамических объектов.

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

Цитата(JackYF @  10.1.2008,  01:06 Найти цитируемый пост)
Функция же должна надеяться только сама на себя - пары, которая вызовется к ней, нет, поэтому должна по возможности содержать объекты локальной области видимости, для которых в случае исключения вызовется деструктор.

Конструктор, пока он не завершится тоже должен надеятся только на себя.

И вообще - всё что ты описал относится к объектам, а не к конструкторам. А пока конструктор не завершился объекта никакого нет.

Цитата(JackYF @  10.1.2008,  01:06 Найти цитируемый пост)
Хотя присутствует небольшой оверхед

Оверхэда нет smile.

Автор: Earnest 10.1.2008, 08:47
Ребята, ну вы и зацепились...
Очевидно, что исключения сами по себе не панацея. И в любой другой функции (не только в конструкторе), если несколько раз выделять память и присваивать ее встроенным указателям, возникновение исключения где-нибудь посредине приведет к утечкам - если не принимать специальных мер. И не только памяти - любой ресурс - открытие файла, создание каких-нибудь объектов ядра и прочаяя. Добавим сюда идиому "Выделение ресурса есть инициализация" и все сразу щастливы. Оверхеды, конечно, есть (у "умных" указателей по сравнения с "глупыми" и прочих оберток), но при нормальном кодировании почти всегда мизерны - скажем, в случае с auto_ptr в релиз-версии будет просто присваивание (а не вызов конструктора) ну и т.д. 
archimed7592 молодец, для 2 часов ночи складно излагаешь smile  

Автор: Lazin 10.1.2008, 09:09
Очевидно, что если в ручную управлять памятью, код будет совсем немного, но быстрей, но это не стоит безопасности по отношению к исключениям, и прочих радостей ручного управления памятью.

Автор: JackYF 10.1.2008, 12:18
Спасибо всем за за объяснения, вопрос закрыт.

Автор: Earnest 10.1.2008, 12:19
Цитата(Lazin @  10.1.2008,  10:09 Найти цитируемый пост)
Очевидно, что если в ручную управлять памятью, код будет совсем немного

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

Автор: Lazin 10.1.2008, 12:27
Earnest, я имею ввиду скорость выполнения кода, так как обычно, оправдание не использования auto_ptr, shared_ptr и т.п. является как-раз якобы имеющее место падение производительности. Лично я стараюсь как можно реже использовать "голые" указатели, и чаще "умные", чего и всем желаю))

Автор: UnrealMan 10.1.2008, 20:12
Цитата(Earnest @  9.1.2008,  20:24 Найти цитируемый пост)
Строго говоря, что в конструкторе исключения бросать, что в других функция - лишь бы не в деструкторе.

А я и в деструкторе не стесняюсь бросать smile

Автор: archimed7592 10.1.2008, 20:14
Цитата(UnrealMan @  10.1.2008,  20:12 Найти цитируемый пост)
А я и в деструкторе не стесняюсь бросать smile

А что дальше? Abnormal termination? Или всё же "корректная" работа? smile

Автор: UnrealMan 10.1.2008, 20:18
А дальше ловим его в том же деструкторе и обрабатываем функтором-обработчиком (при этом деструктор не знает, как именно должна происходить обработка исключения).

Автор: bsa 10.1.2008, 20:23
Цитата(UnrealMan @ 10.1.2008,  20:18)
А дальше ловим его в том же деструкторе и обрабатываем функтором-обработчиком (при этом деструктор не знает, как именно должна происходить обработка исключения).

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

Автор: JackYF 10.1.2008, 20:23
UnrealMan, да, ты нереальный человек smile

Автор: archimed7592 10.1.2008, 20:25
Цитата(UnrealMan @  10.1.2008,  20:18 Найти цитируемый пост)
А дальше ловим его в том же деструкторе и обрабатываем функтором-обработчиком (при этом деструктор не знает, как именно должна происходить обработка исключения).

Как же я сразу не догадался в чём подвох smile.

В принципе, для записи логов и т.п. решение очень даже красивое(тот самый throw; во внешней ф-ции, да? smile ).

Автор: baldina 10.1.2008, 20:27
Цитата

1. деструктор вызовется
2. деструктор не вызовется

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

Добавлено через 3 минуты и 1 секунду
ступил - до конца не дочитал  smile

Добавлено через 6 минут и 22 секунды
главного то не написал:
надо бросить - бросаю. 
хотя конечно не ожидая исключений как-то комфортнее

Автор: UnrealMan 10.1.2008, 20:48
Цитата(archimed7592 @  10.1.2008,  20:25 Найти цитируемый пост)
В принципе, для записи логов и т.п. решение очень даже красивое(тот самый throw; во внешней ф-ции, да?  smile. 

Ну, да. Допустим, объект по уничтожении должен сбросить некоторую инфу в файл. Но по какой-то причине запись в файл оказалась невозможной (файл заблокирован, не хватило места на диске). Мы можем попросить пользователя закрыть кое-какие программы или освободить место на диске. Как именно обращаться к пользователю, классу уничтожаемого объекта знать не обязательно, ибо это не его дело. Достаточно, чтобы деструктор откуда-то получил функциональный объект, обрабатывающий исключения (например, он может быть сохранён в объекте при конструировании). При этом возможны две ситуации: объекту-обработчику (при помощи доброго пользователя) удалось разрешить проблему, либо не удалось. Если удалось, мы можем повторить попытку записи в файл. Если не удалось, просто выходим из деструктора (какие-то данные будут потеряны, но такова уж судьба - ничего лучше сделать не смогли).

Описание handler-а и деструктора выглядит примерно так:

Код

struct Handler
{
    void operator ()(параметры)
    {
         try
         {
              throw;
         }
         catch (поехали обрабатывать исключения)
         {
         }
         ....
    }
};

~T()
{
    try
    {
        for (;;)
            try
            {
                ...
            }
            catch (...)
            {
                // handler имеет тип boost::function<void (параметры)>
                handler(параметры); // может бросить исключение в виде объекта класса HandlingFailed
            } // если не бросает, повторяем попытку :-)
    }
    catch (HandlingFailed &)
    {
    }
}

Кстати, конструктор тоже может следовать этой методике. Зачем сразу сдуваться, если можно вежливо попросить пользователя разрешить проблему и доконструроваться себе дальше? smile

Автор: JackYF 10.1.2008, 21:12
UnrealMan, честно говоря, не понял сиих конструкций.  smile .
При возникновении нештатной ситуации в деструкторе я лучше сделаю запись в логах, что всё плохо или не очень, и выйду из деструктора.

Автор: UnrealMan 10.1.2008, 21:19
Цитата(JackYF @  10.1.2008,  21:12 Найти цитируемый пост)
При возникновении нештатной ситуации в деструкторе я лучше сделаю запись в логах, что всё плохо или не очень, и выйду из деструктора.

Зачем так пессимистично поступать? Вдруг нештатную ситуацию удастся разрешить и во вполне штатном режиме выполнить функции деструктора?

Добавлено @ 21:20
Цитата(JackYF @  10.1.2008,  21:12 Найти цитируемый пост)
UnrealMan, честно говоря, не понял сиих конструкций. 

Шо не понятно? Выбросили исключение, поймали, вызвали обработчик, в обработчике исключение сгенерировали повторно и обработали smile

Добавлено @ 21:23
Класс, которому принадлежит деструктор, об обработчике почти ничего не знает - т.е., например, способ обращения к пользователю не будет захардкоден в деструктор, и это есть хорошо smile 

Автор: JackYF 10.1.2008, 21:43
Цитата(UnrealMan @  10.1.2008,  20:19 Найти цитируемый пост)
Шо не понятно?

Как эта штука будет масштабироваться на несколько деструкторов. Да и вообще - мой мозг на сейчас отказывается воспринимать эту структуру кода. 

Автор: archimed7592 10.1.2008, 21:47
Цитата(JackYF @  10.1.2008,  21:43 Найти цитируемый пост)
Да и вообще - мой мозг на сейчас отказывается воспринимать эту структуру кода.

Запусти:
Код

void f1();
void f2()
{
  try
  { throw 0; }
  catch(...)
  { f1(); }
}

void f1()
{
  try
  { throw; }
  catch(...)
  { std::cout << "hi" << std::endl; }
}

Автор: UnrealMan 10.1.2008, 22:01
Цитата(JackYF @  10.1.2008,  21:43 Найти цитируемый пост)
Как эта штука будет масштабироваться на несколько деструкторов.

Тут не стоит такая задача.
Желательно, чтобы обработка исключений и функционал деструктора были разнесены, а не перемешивались (знакомая парадигма, не так ли?). Вот на это данный код и нацелен.

Автор: JackYF 10.1.2008, 22:02
Цитата(archimed7592 @  10.1.2008,  20:47 Найти цитируемый пост)
try
  { throw; }
  catch(...)

так, уже лучше. Каково предназначение этого куска кода?

Автор: UnrealMan 10.1.2008, 22:27
Цитата(JackYF @  10.1.2008,  22:02 Найти цитируемый пост)
так, уже лучше. Каково предназначение этого куска кода? 

Т.к. мы хотим заставить исключения обрабатываться в этих catch-блоках (внутри функции/функтора-обработчика), то исключение надо генерировать внутри соответствующего try-блока. А поскольку изначально исключение генерируется не в этом try-блоке, а в другом месте (где-то в деструкторе), то нужно поймать это исключение и повторно сгенерировать его внутри данного try-блока, а иначе внутрь этого try-блока оно никак не попадёт. Я понятно выражаюсь? smile

Автор: archimed7592 10.1.2008, 22:29
Цитата(JackYF @  10.1.2008,  22:02 Найти цитируемый пост)
Каково предназначение этого куска кода? 

Эммм, ок, дабы немного придать смысл коду:
Код

void handler();
void foo()
{
  try
  {
      if (std::rand() % 2)
        throw 0;
      else
        throw "0";
  }
  catch(...)
  { handler(); }
}
void handler()
{
  try
  { throw; }
  catch(int i)
  { std::cout << "int = " << i << std::endl; }
  catch(const char *s)
  { std::cout << "string = " << s  << std::endl; }
  catch(...)
  { std::cout << "unknown" << std::endl; }
}

Автор: JackYF 10.1.2008, 22:34
Теперь понял, спасибо вам обоим.

Автор: Mayk 11.1.2008, 20:14
есть мнение что в подобном случае следует заводить ф-цию init и вызывать её, дабы в случае чего деструктор уничтожал объект.
вообще стараюсь исключения не кидать. 
а если кидать то в основном из ASSERT'ов.

Автор: vadiml 12.1.2008, 16:12
как писали выше, можно заворачивать вызов конструктора. Правда я для этого не пишу специальную статическую функцию. Такое надо очень редко и хватает обычной функции, которая вернёт указатель или NULL, а далее if ( retval == NULL ) { что-то по этому поводу сделать }. Так же в такую функцию часто добавляю вывод в stderr, что бы сразу это видеть.


Автор: chipset 21.1.2008, 05:29
Пишу специальное исключение. Если не сгенерировался обьект то лучше всего убить программу пока не поздно и записать в лог подробности. Допустим на серверах так и делается обычно.

А что-бы избежать ситуации: имеем два обьекта А и Б, А инициализирован, Б нет -- чо делать ё? стараюсь что-бы у каждого обьекта был только один указатель на данные которые он обрабатывает.

Это выглядит вот так:

Код

class IntArray : ReferenceCountedStorage<vector<int> > 
{
/* vector<int> *m_dataPtr; //taken care by RCS */
public:
//функции которые работают с m_dataPtr
};

Автор: UnrealMan 25.1.2008, 15:05
Цитата(chipset @  21.1.2008,  05:29 Найти цитируемый пост)
А что-бы избежать ситуации: имеем два обьекта А и Б, А инициализирован, Б нет -- чо делать ё?

Использовать smart pointer-ы или try + catch.

Автор: Lycifer 4.8.2008, 16:32
Вообще исключения в конструкторах очень опастно
пример
class A
{
public:
      A()
    {
throw 1;
    }
};
class B : A
{
public:
      B()
    {

    }
};

решение проблемы

class A
{
protected:
bool IsCreateBad;
A(bool)
{
}
public:
      A():IsCreateBad(false)
    {
throw 1;
    }
bool getCreateObject()
{
return IsCreateBad;
}
};
class B : A
{
protected:
B(bool isCreateBad) 
    {
    }
public:
      B() try
    {
    }
catch(...)
{
(*this) = B(true);
}
};

Добавлено через 11 минут и 46 секунд
Ну почемуто возникает assert вопрос почему?

Автор: bsa 4.8.2008, 23:31
Lycifer, проблемы нет там, где ты думаешь, что она есть. Так как если конструктор кидает исключение, значит он не смог сконструировать объект класса, значит конструктор потомка не должен даже запускаться.

Автор: SABROG 5.8.2008, 00:07
Так забавно наблюдать за зверушками, которые копашатся в клетке пытаясь решить какие-то проблемы. Сразу вспоминается Си и ассемблер, где вся ответственность ложится на тебя. И в качестве бонуса дается некоторая порция свободы действий.

По теме. Исключения почти никогда не использовал, т.к. небыло необходимости. Обычно проблемы решаются достаточно тривиальными проверками на уровне возвращаемого значения true или false. Уж не знаю как вы, а для меня исключения это не более чем int 3 или деление на 0, а остальное от лукавого.

Автор: vinter 5.8.2008, 07:22
Цитата(SABROG @  5.8.2008,  01:07 Найти цитируемый пост)
Уж не знаю как вы, а для меня исключения это не более чем int 3 или деление на 0, а остальное от лукавого. 
--------------------

значит ты просто не понимаешь исключений.

Автор: W4FhLF 5.8.2008, 08:33
Цитата(SABROG @  5.8.2008,  00:07 Найти цитируемый пост)
 Исключения почти никогда не использовал, т.к. небыло необходимости.


Это тебя не красит. smile 

Почитай http://forum.vingrad.ru/forum/topic-216818.html

Автор: Lycifer 5.8.2008, 09:49
Цитата

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

А потом оброщение к не существующей памяти, или try и catch где объект создаётся ставить?(Лучше это сделать в класе вот только не получилось  smile ,почему не могу понять)

Автор: vinter 5.8.2008, 10:34
Цитата(Lycifer @  5.8.2008,  10:49 Найти цитируемый пост)
А потом оброщение к не существующей памяти, или try и catch где объект создаётся ставить?

без try\catch будет вызван termination(); и программа упадет.

Автор: Partizan 5.8.2008, 10:53
Кстати говоря, двухфазные конструкторы в Symbian решают аналогичные проблемы...

Автор: Torsten 5.8.2008, 11:27
Цитата
стараюсь не бросать исключений в конструкторах, но иногда приходится

хотя в основном, практически всегда создаю метод Init, где и произвожу все сложную инициализацию, которая может привести к проблемам.
Вообще исключение не люблю, код от них бухнет и его трудно читать, поэтому стараюсь их всегда избегать.

Автор: Lazin 5.8.2008, 11:39
Цитата(Torsten @  5.8.2008,  11:27 Найти цитируемый пост)
Вообще исключение не люблю, код от них бухнет и его трудно читать, поэтому стараюсь их всегда избегать.

еще один пациент smile 

Автор: Lycifer 5.8.2008, 12:41
Цитата

без try\catch будет вызван termination(); и программа упадет. 
 - и это хорошо?

Автор: Peter 5.8.2008, 13:03
Цитата(Mayk @  11.1.2008,  20:14 Найти цитируемый пост)
есть мнение что в подобном случае следует заводить ф-цию init и вызывать её, дабы в случае чего деструктор уничтожал объект

Цитата(Torsten @  5.8.2008,  11:27 Найти цитируемый пост)
хотя в основном, практически всегда создаю метод Init, где и произвожу все сложную инициализацию, которая может привести к проблемам

И я так же поступаю. Конструкторы у меня ничего опасного не делают. Всё опасное выносится в функцию инициализации - а ей-то уж возвращать значение не запрещается.

Автор: UnrealMan 5.8.2008, 14:16
Цитата(Lycifer @  5.8.2008,  12:41 Найти цитируемый пост)
- и это хорошо?

Это хорошо.

А вот создавать невалидный объект и потом его инициализировать, притом не допуская иного способа инициализации объекта - это определённо нехорошо.

Автор: Torsten 5.8.2008, 16:14
Цитата(Lazin @  5.8.2008,  11:39 Найти цитируемый пост)
еще один пациент  

а чего это я больным стал ?

Автор: vinter 5.8.2008, 16:24
Цитата(Torsten @  5.8.2008,  17:14 Найти цитируемый пост)
а чего это я больным стал ?

паническая болезнь исключений smile

Автор: Lazin 5.8.2008, 18:23
Цитата(Torsten @  5.8.2008,  16:14 Найти цитируемый пост)
а чего это я больным стал ?

будем тебя учить любить исключения smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)