Re[5]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 07.07.10 14:42
Оценка: -3
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Аноним, Вы писали:


А>>А почему вам так кажется? Финализатор в любом случае принято писать устойчивым к повторном вызовам.

0>Речь совсем не о повторных вызовах.
Написание такого финализатора очень похоже на реализацию финализатора для неполностью сконструированного объекта.

Короче — подсказываю — вам так кажется, потому что вы к этому привыкли. А на самом деле — в c++ тут допущена ошибка дизайна, которую подправили в .net.
Re[6]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:44
Оценка: +2 -1
Здравствуйте, Аноним, Вы писали:

А>Короче — подсказываю — вам так кажется, потому что вы к этому привыкли. А на самом деле — в c++ тут допущена ошибка дизайна, которую подправили в .net.

Не согласен. Я считаю, что С++-ный подход вернее. Мои аргументы заключается в том, что тамошняя система в бОльшей степени сохраняет гарантии программисту относительно целостности объекта при вызове его методов (в т.ч. и деструктора). Это, кстати, относится и к возможности вызывать виртуальные методы в конструкторе.
Почему Вы считаете, что это неверный подход?
Re[6]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 17:03
Оценка: +2
Здравствуйте, Mystic, Вы писали:


M>Ну смотри. Такой код смотрится красиво, но с ним будут проблемы:

Это, скажем прямо, не лучший стиль написания кода. И он по определению не exception-safe.
Лучше так:
  class test_t
  {
    private:
      std::auto_ptr<inner_test_t> test1;
      std::auto_ptr<inner_test_t> test2;
    public:
      test_t()
      {
        // Код, который может привести к исключению
        test1 = new inner_test_t();
        // Код, который может привести к исключению
        test2 = new inner_test_t(); 
        // Код, который может привести к исключению
      }  
  };


M>Поэтому приходится либо чрезмерно усложнять конструктор,

Где чрезмерное усложнение?

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

Если не иметь навыков работы с исключениями, то они в принципе станут штукой стрёмной. Exception-unsafe-код можно написать везде. Некоторые, кстати, принципиально отказываются от механизма исключений и имеют на это право. Но если с уметь с исключениями работать, то исключения в конструкторе становятся не более стремными, чем в любом другом месте программы.

Вынос инициализации в отдельный метод — это вопрос отдельный и я его с исключениями никак не связываю.
Re[5]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 07.07.10 14:37
Оценка: 3 (1)
Здравствуйте, 0x7be, Вы писали:

0>Финализаторы успешно сконструированных предков должны вызываться.

Здесь, есть еще такой ньюанс — в managed языках создание объекта идет, не как в C++ от предка к родителю, а фактически наоборот (как в java):
class X : Base { public X(int x) { super(x); } }

B Eсли например, вызвать в конструкторе предка виртуальный метод переопределенный в потомке, то вызовется метод потомка. Т.е. с точки зрения среды гораздо сложнее сообразить, кто уже сконструирован, а кто нет.
Re[5]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 07.07.10 16:38
Оценка: 1 (1)
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Mystic, Вы писали:


M>>В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера.

0>С выделенным не согласен. Это очень логичное поведение, целиком укладывающееся в идею, что язык предоставляет программисту ряд инвариантов.

Ну смотри. Такой код смотрится красиво, но с ним будут проблемы:

  class test_t
  {
    private:
      inner_test_t* test1;
      inner_test_t* test2;
    public:
      test_t()
      {
        // Код, который может привести к исключению
        test1 = new inner_test_t();
        // Код, который может привести к исключению
        test2 = new inner_test_t(); 
        // Код, который может привести к исключению
      }  

      virtual ~test_t()
      {
        if (test1) delete test1;
        if (test2) delete test2;
      }
  };


Поэтому приходится либо чрезмерно усложнять конструктор, либо признать, что исключения в конструкторе штука стремная, и ее лучше инициализацию вынести в отдельный метод. Собственно говоря, ради возможности такой удобной записи все и затеяно начиная с Delphi.
Re[6]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 09:01
Оценка: 1 (1)
Здравствуйте, Mystic, Вы писали:

M>>>В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера.

0>>С выделенным не согласен. Это очень логичное поведение, целиком укладывающееся в идею, что язык предоставляет программисту ряд инвариантов.

M>Ну смотри. Такой код смотрится красиво, но с ним будут проблемы:


Если код ниже — это С++, то нафига в деструкторе проверки на 0
M>      virtual ~test_t()
M>      {
M>        delete test1;
M>        delete test2;
M>      }


M>Поэтому приходится либо чрезмерно усложнять конструктор,


Те проблемы, о которых ты говоришь, к С++ имеют весьма опосредованное значение, ибо в С++ помимо сишных указателей есть и более умные

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


Исключение в конструкторе — штука более чем ясная.

M>Собственно говоря, ради возможности такой удобной записи все и затеяно начиная с Delphi.


Можно увидеть пруфлинк?
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:10
Оценка: +1
Здравствуйте, nikov, Вы писали:

N>А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать?

Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.
Деструкторы там вызываются только для полностью сконструированных объектов (подобъектов). Ошибки в процессе конструирования должны обрабатываться самим конструктором.
Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 13:57
Оценка:
Коллеги!

Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.
Re: Исключение в конструкторе + финализатор
От: nikov США http://www.linkedin.com/in/nikov
Дата: 07.07.10 14:08
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.


А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать?
Re[3]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 07.07.10 14:25
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, nikov, Вы писали:


N>>А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать?

0>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.

А почему вам так кажется? Финализатор в любом случае принято писать устойчивым к повторном вызовам.
Re[3]: Исключение в конструкторе + финализатор
От: samius Япония http://sams-tricks.blogspot.com
Дата: 07.07.10 14:26
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, nikov, Вы писали:


N>>А вдруг конструктор успел захватить какие-то unmanaged ресурсы до того как кинул исключение. Если не вызывать финализатор, то кто будет их освобождать?

0>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.
Дублирование получается.
Re[4]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:28
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А почему вам так кажется? Финализатор в любом случае принято писать устойчивым к повторном вызовам.

Речь совсем не о повторных вызовах.
Re[3]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 07.07.10 14:29
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Пускай он их и освобождает.

А если исключение бросает конструктор класса-наследника, то как родителю освободить ресурс?
Re[4]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:29
Оценка:
Здравствуйте, samius, Вы писали:

0>>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.

S>Дублирование получается.
Дублирование устраняется другими методами.
Re[4]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:32
Оценка:
Здравствуйте, Аноним, Вы писали:

0>>Пускай он их и освобождает.

А>А если исключение бросает конструктор класса-наследника, то как родителю освободить ресурс?
Финализаторы успешно сконструированных предков должны вызываться.
Re[3]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 07.07.10 14:34
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Пускай он их и освобождает. Мне более правильно кажется логика конструирования/разрушения принятая в C++.


В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера.

0>Ошибки в процессе конструирования должны обрабатываться самим конструктором.


Это неудобно, потому что усложняет конструктор.
Re[4]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:38
Оценка:
Здравствуйте, Mystic, Вы писали:

M>В C++ данные могут быть непроинициализированными. Почему деструктор никак не сможет установить, что конкретно было проинициализировано, а где просто мусор. Так что это вынужденная мера.

С выделенным не согласен. Это очень логичное поведение, целиком укладывающееся в идею, что язык предоставляет программисту ряд инвариантов.

0>>Ошибки в процессе конструирования должны обрабатываться самим конструктором.

M>Это неудобно, потому что усложняет конструктор.
Количество сложности не изменяется. Усложняется конструктор, упрощается финализатор.
Re[6]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:39
Оценка:
Здравствуйте, Аноним, Вы писали:

А>B Eсли например, вызвать в конструкторе предка виртуальный метод переопределенный в потомке, то вызовется метод потомка.

А это второй большой глюк, за который авторам С# надо что-нибудь оторвать
Re[7]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 07.07.10 14:43
Оценка:
Здравствуйте, 0x7be, Вы писали:
0>А это второй большой глюк, за который авторам С# надо что-нибудь оторвать
Это не баг, а фича Причем не C#, а всего .net, и как я уже говорил, специфичная не только для .net.
Видимо, эта "фича" что-то упрощает для среды исполнения управляемых языков, самому если честно всегда было интересно — что конкретно.
Re[8]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 14:46
Оценка:
Здравствуйте, Аноним, Вы писали:

0>>А это второй большой глюк, за который авторам С# надо что-нибудь оторвать

А>Это не баг, а фича Причем не C#, а всего .net, и как я уже говорил, специфичная не только для .net.
А>Видимо, эта "фича" что-то упрощает для среды исполнения управляемых языков, самому если честно всегда было интересно — что конкретно.
Не только для .net. В Дельфи, например, тоже же самое творится. Но там еще к тому же можно:
1. Создавать экземпляры абстрактных классов.
2. Не вызывать конструктор/деструктор базового класса.
3. Подёргать методы базового класса ДО вызова его конструктора или ПОСЛЕ вызова его деструктора

Только за такие "фитчи" дизайнерам языка надо что-то очень плохое сделать
Re: Исключение в конструкторе + финализатор
От: hardcase Пират http://nemerle.org
Дата: 07.07.10 15:10
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Коллеги!


0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.


Откровенно говоря, конструктор для CLR уже отработал — память выделена, поля имеют дефолтные значения: null для ссылок, и нули для структур. Конструкторы, с которыми мы имеем дело в языках всего лишь производят инициализацию полей экземпляра, т.е являются всего лишь автоматически вызваемым методом вертающим void (как вы верно заметили в Delphi похожее поведение). Посему вызов финализатора — вполе оправданное действие, ведь код финализатора (грубо говоря Dispose) обязан работать как для неполностью созданного объекта, так и для уже "разрушенного" объекта.
/* иЗвиНите зА неРовнЫй поЧерК */
Re: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 07.07.10 15:50
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Коллеги!

0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.

Просто другая организация работы с памятью. Отсюда и другие правила поведения. В С++ правильно одно в силу стандарта С++ и ничего более. В дотнете и шарпе правильно своё в силу местных стандартов.
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 16:55
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Просто другая организация работы с памятью. Отсюда и другие правила поведения. В С++ правильно одно в силу стандарта С++ и ничего более. В дотнете и шарпе правильно своё в силу местных стандартов.

Я не ставил вопрос о соответствии реализации C# его спецификации
Я ставлю вопрос о том, насколько разумны отдельные положения этой спецификации.
Re[7]: Исключение в конструкторе + финализатор
От: hardcase Пират http://nemerle.org
Дата: 07.07.10 17:11
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Это, скажем прямо, не лучший стиль написания кода. И он по определению не exception-safe.

0>Лучше так:

Вот и для 99% классов код и будет аналогичным в C#.
Заострять внимание на финализаторе нужно только при работе с неуправляемыми ресурсами, о чем сразу же написал nikov.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[9]: Исключение в конструкторе + финализатор
От: Jack128  
Дата: 07.07.10 18:40
Оценка:
Здравствуйте, 0x7be, Вы писали:
>1. Создавать экземпляры абстрактных классов.
тут чисто техническая проблема. В дельфи на уровне компилятора просто невозможно запретить создание абстрактных классов, потому что там есть классовые ссылки..
Re[10]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 07.07.10 19:21
Оценка:
Здравствуйте, Jack128, Вы писали:

J>тут чисто техническая проблема. В дельфи на уровне компилятора просто невозможно запретить создание абстрактных классов, потому что там есть классовые ссылки..

Не понял. Классовая ссылка — это ссылка на экземпляр метакласса?
Чем это мешает запрещать создание экземпляров абстрактного класса?
В C# (а, точнее, в .net) тоже есть класс Type, который суть метакласс, но даже с его помощью такого не сделаешь.
При попытке динамически вызвать конструктор вылетит птич... исключение
Re[7]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 07.07.10 22:03
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Почему Вы считаете, что это неверный подход?


Да не то, чтобы неверный. Просто неоптимальный.
Про С спорить не буду, но в .net-е вы обязаны писать финализатор так, чтобы он был устойчив к повторному вызову и ошибкам внутри самого финализатора. От этого к финализатору, устойчивому к ошибкам инициализации — пол-шага. Т.е. он получается практически на халяву.

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

А вред-то где?
Re[3]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 08.07.10 04:12
Оценка:
0x7be,

_FR>>Просто другая организация работы с памятью. Отсюда и другие правила поведения. В С++ правильно одно в силу стандарта С++ и ничего более. В дотнете и шарпе правильно своё в силу местных стандартов.

0>Я не ставил вопрос о соответствии реализации C# его спецификации
0>Я ставлю вопрос о том, насколько разумны отдельные положения этой спецификации.

За возможность вызвать виртуальный метод в конструкторе — моя личная благодарность создателям и особенно тов. Хейлсбергу

Как ты понимаешь, эта фича была бы невозможна, если бы термин "конструктор" в C# (или в CLR) семантически совпадал с термином "конструктор" в C++.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[7]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 08:55
Оценка:
Здравствуйте, 0x7be, Вы писали:

А>>Короче — подсказываю — вам так кажется, потому что вы к этому привыкли. А на самом деле — в c++ тут допущена ошибка дизайна, которую подправили в .net.

0>Не согласен. Я считаю, что С++-ный подход вернее. Мои аргументы заключается в том, что тамошняя система в бОльшей степени сохраняет гарантии программисту относительно целостности объекта при вызове его методов (в т.ч. и деструктора). Это, кстати, относится и к возможности вызывать виртуальные методы в конструкторе.
0>Почему Вы считаете, что это неверный подход?

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

С какой стати файнализатор вообще должен вести себя как деструктор и с какой смысл имеет требование от него такого поведения

Это две совсем разные вещи, начиная с того, что предназначены для различных операций. Файнализатор — всего лишь место для освобождения неуправляемых ресурсов. С появлением SafeHandle-ов необходимость в них пропала совсем (если не принимать во внимание экзотические случаи).

Если в С++ деструктор является необходимым атрибутом большого количества объектов, то в шарпе это совсем не так.

Если заняться нечем, можно ещё порассуждать на тему того, чего это файнализаторы вызываются не как деструкторы в С++, а вообще в специальном отдельном потоке
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 09:04
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

_FR>>>Просто другая организация работы с памятью. Отсюда и другие правила поведения. В С++ правильно одно в силу стандарта С++ и ничего более. В дотнете и шарпе правильно своё в силу местных стандартов.

0>>Я не ставил вопрос о соответствии реализации C# его спецификации
0>>Я ставлю вопрос о том, насколько разумны отдельные положения этой спецификации.

LCR>За возможность вызвать виртуальный метод в конструкторе — моя личная благодарность создателям и особенно тов. Хейлсбергу


Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?

LCR>Как ты понимаешь, эта фича была бы невозможна, если бы термин "конструктор" в C# (или в CLR) семантически совпадал с термином "конструктор" в C++.


+1.
Help will always be given at Hogwarts to those who ask for it.
Re[8]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 08.07.10 09:20
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Если заняться нечем, можно ещё порассуждать на тему того, чего это файнализаторы вызываются не как деструкторы в С++, а вообще в специальном отдельном потоке

Ну это как раз не большая загадка
В целом мне твой поинт понятен.
Re[8]: Исключение в конструкторе + финализатор
От: blackhearted Украина  
Дата: 08.07.10 09:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, 0x7be, Вы писали:


0>>Почему Вы считаете, что это неверный подход?


А>Да не то, чтобы неверный. Просто неоптимальный.

А>Про С спорить не буду, но в .net-е вы обязаны писать финализатор так, чтобы он был устойчив к повторному вызову и ошибкам внутри самого финализатора. От этого к финализатору, устойчивому к ошибкам инициализации — пол-шага. Т.е. он получается практически на халяву.

Речь не про С, а про С++.
Re[7]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 09:37
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Mystic, Вы писали:



M>>Ну смотри. Такой код смотрится красиво, но с ним будут проблемы:

0>Это, скажем прямо, не лучший стиль написания кода. И он по определению не exception-safe.
0>Лучше так:

Это я понимаю, это просто иллюстрация основной идеи. Более реальный пример, например, открытие файла. Создание какого-нить объекта ядра. Память была просто в качестве примера некоторого ресурса, поэтому я так и писал код. Замени test1 и test2 на что-нить вроде

  _file = open(filepath.c_str(), O_RDONLY);
  if (_file < 0) throw e();

  if (fstat(_file, &stat_buf) < 0) throw e(); // close(_file); 
  _size = stat_buf.st_size;

  _memory = mmap(0, _size, PROT_READ, MAP_PRIVATE, _file, 0);
  if (_memory == NULL || _memory == MAP_FAILED) throw e(); // close(_file)

  // Продолжаем инициализацию объекта


0>Если не иметь навыков работы с исключениями, то они в принципе станут штукой стрёмной. Exception-unsafe-код можно написать везде. Некоторые, кстати, принципиально отказываются от механизма исключений и имеют на это право. Но если с уметь с исключениями работать, то исключения в конструкторе становятся не более стремными, чем в любом другом месте программы.


Чисто субъективно, в том же Delphi проще. Как раз из-за автоматического вызова деструктора и автоматической инициализации.
Re[7]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 09:43
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Те проблемы, о которых ты говоришь, к С++ имеют весьма опосредованное значение, ибо в С++ помимо сишных указателей есть и более умные


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

M>>Собственно говоря, ради возможности такой удобной записи все и затеяно начиная с Delphi.


_FR> Можно увидеть пруфлинк?


constructor TClass.Create();
begin
  inherited;

  FFile1 := CreateFile(...);
  Check(FFile1 <> INVALID_HANDLE_VALUE, 'CreateFile fails');

  FFile2 := CreateFile(...);
  Check(FFile2 <> INVALID_HANDLE_VALUE, 'CreateFile fails');
end;  

destructor TClass.Destroy();
begin
  if IsValid(FFile1) then CloseHandle(FFile1);
  if IsValid(FFile2) then CloseHandle(FFile2);
  inherited;
end;
Re[9]: Исключение в конструкторе + финализатор
От: Аноним  
Дата: 08.07.10 09:44
Оценка:
Здравствуйте, blackhearted, Вы писали:
B>Речь не про С, а про С++.
Вах! Ну надо же! Ну кто бы мог подумать!
P.S. У меня на ноуте кнопочку + лежа нажимать неудобно.
Re[8]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 09:55
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>Те проблемы, о которых ты говоришь, к С++ имеют весьма опосредованное значение, ибо в С++ помимо сишных указателей есть и более умные

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

При чём здесь другие языки? Речь конкретно о С++.

M>>>Собственно говоря, ради возможности такой удобной записи все и затеяно начиная с Delphi.


_FR>> Можно увидеть пруфлинк?


M>constructor TClass.Create();


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

P.S. Неужели менее уродским способом нельзя в дельфях то же самое написать?
Help will always be given at Hogwarts to those who ask for it.
Re[9]: Исключение в конструкторе + финализатор
От: hardcase Пират http://nemerle.org
Дата: 08.07.10 10:20
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>P.S. Неужели менее уродским способом нельзя в дельфях то же самое написать?


Можно. Надо только TFileStream использовать.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[5]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:22
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


Например, в VCL при вызове конструктора формы в конце концов вызовется метод виртуальный CreateParams, в котором можно будет настроить параметры вызовов RegisterClass и CreateWindow.
Re[10]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 10:24
Оценка:
Здравствуйте, hardcase, Вы писали:

_FR>>P.S. Неужели менее уродским способом нельзя в дельфях то же самое написать?


H>Можно. Надо только TFileStream использовать.


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

Тогда, что бы придать нормальности тому коду, не хватает только метода, принимающего объект, проверяющего — не nil (так, кажется?) ли он и вызывающей какой-нить Close() или Dispose().
Help will always be given at Hogwarts to those who ask for it.
Re[11]: Исключение в конструкторе + финализатор
От: hardcase Пират http://nemerle.org
Дата: 08.07.10 10:26
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Тогда, что бы придать нормальности тому коду, не хватает только метода, принимающего объект, проверяющего — не nil (так, кажется?) ли он и вызывающей какой-нить Close() или Dispose().


FreeAndNil(FFile);


Где FFile это поле. Процедура грохнет объект если он не равен nil и занулит ссылку.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[6]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 10:27
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


M>Например, в VCL при вызове конструктора формы в конце концов вызовется метод виртуальный CreateParams, в котором можно будет настроить параметры вызовов RegisterClass и CreateWindow.


А в FCL прекрасно обходится без этого Откуда необходимость вызывать CreateWindow непосредственно в конструкторе?
Help will always be given at Hogwarts to those who ask for it.
Re[9]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:32
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>1. Создавать экземпляры абстрактных классов.

В Delphi нет абстрактных классов, есть только абстрактные методы.

0>2. Не вызывать конструктор/деструктор базового класса.

Это следствие того, что можно динамически вызывать любой конструктор/деструктор базового класса в зависимости от необходимости.

0>3. Подёргать методы базового класса ДО вызова его конструктора или ПОСЛЕ вызова его деструктора

См. п. 2


Вообще, если брать аналоги конструктора/деструктора в C++, то в Delphi это скорее всего методы NewInstance и FreeInstance. Именно они создают объект. А конструктор это уже метод, который инициализирует уже созданный объект. Поэтому конструктор может быть виртуальным и может дергать виртуальные методы.
Re[9]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:39
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Мне интересно взглянуть на подтверждение того, что "затеяно" именно "ради возможности такой удобной записи".


Был затеян автоматический вызов деструктора, в случае исключения в конструкторе. Это позволяет не думать о том, как освобождать объекты в конструкторе в случае, когда надо кинуть исключение (возникло исключение). Это очень удобная форма записи: в конструкторе только инициализация без всякого освобождения ресурсов в случае фатального завершения.
Re[5]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 08.07.10 10:44
Оценка:
_FRED_,

LCR>>За возможность вызвать виртуальный метод в конструкторе — моя личная благодарность создателям и особенно тов. Хейлсбергу


_FR>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


Да, конечно, без проблем.
public interface IScope
{
    void Wrap(Action action);
}

public class Parent
{
    protected readonly IScope Scope;
    public Parent()
    {
        Scope = CreateLocalScope();
    }

    protected virtual IScope CreateLocalScope()
    {
        return new SimpleScope();
    }

    public virtual void UseMe()
    {
        Scope.Wrap(() =>
        {
            // empty
        });
    }
}

public class Child1 : Parent
{
    protected override IScope CreateLocalScope()
    {
        return new TransactionalScope();
    }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is inside a transaction
            // ...
        });
    }
}

public class Child2 : Parent
{
    protected override IScope CreateLocalScope()
    {
        return new RepeatedScope(3);
    }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is repeated 3 times
            // and throw an exception in case of failure
            // ...
        });
    }
}

//------------------------------//    
var structure = new[]
{
    new Parent(),
    new Child1(),
    new Child2(),
};
foreach (var el in structure)
    el.UseMe();

В моём случае у меня были требования — класс должен иметь конструктор по-умолчанию. Кроме того, оказалось гораздо удобнее позволить классу-наследнику Parent самому конфигурить себя, вместо того чтобы городить ioc-stuff. Без виртуальности в конструкторе вызов new Child1() создавал бы класс, но он бы пребывал бы какое-то время в невалидном состоянии (пока ему, скажем, не сделают Scope=..), что вряд ли бы добавило радости впоследствии.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[11]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:44
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, hardcase, Вы писали:


_FR>Ага, мне тоже показалось странным, что в дельфях нету нормальной (здесь: бросающей исключения) обёртки над CreateFile.


А мне нужен именно CreateFile. Мне надо устанавливать всякие флаги, начиная от SecurityAttributes и заканчивая FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED и т. п. В конце концов это может быть любой другой вызов API.
Re[6]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 08.07.10 10:48
Оценка:
LCR>..., что вряд ли бы добавило радости впоследствии.
"радости сопровождающему товарищу" имелось ввиду.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[7]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 10:51
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>А в FCL прекрасно обходится без этого Откуда необходимость вызывать CreateWindow непосредственно в конструкторе?


Удобно, когда объединены создание и инициализация Чем это плохо? Вполне возможный вариант.
Re[12]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:22
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>Ага, мне тоже показалось странным, что в дельфях нету нормальной (здесь: бросающей исключения) обёртки над CreateFile.


M>А мне нужен именно CreateFile. Мне надо устанавливать всякие флаги, начиная от SecurityAttributes и заканчивая FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED и т. п. В конце концов это может быть любой другой вызов API.


И что тебе мешает сначала сделать нормальную обёртку а потом уже нормально пользоваться ей вместо того что бы использовать как есть и говорить "как неудобно!"
Help will always be given at Hogwarts to those who ask for it.
Re[10]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:28
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>Мне интересно взглянуть на подтверждение того, что "затеяно" именно "ради возможности такой удобной записи".


M>Был затеян автоматический вызов деструктора, в случае исключения в конструкторе. Это позволяет не думать о том, как освобождать объекты в конструкторе в случае, когда надо кинуть исключение (возникло исключение). Это очень удобная форма записи: в конструкторе только инициализация без всякого освобождения ресурсов в случае фатального завершения.


В третий раз закинул дед невод: пруфлинк можно получить на то, где _разработчики_ языка (которые, собственно, и "затевали") говорят, что вызов дестуктора даже в случае возникновения ошибки в конструкторе обусловлен именно этими причинами?
Help will always be given at Hogwarts to those who ask for it.
Re[8]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:30
Оценка:
Здравствуйте, Mystic, Вы писали:

_FR>>А в FCL прекрасно обходится без этого Откуда необходимость вызывать CreateWindow непосредственно в конструкторе?


M>Удобно, когда объединены создание и инициализация Чем это плохо? Вполне возможный вариант.


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

А тебе доподлинно известно, что CreateWindow происходит именно в конструкторе?
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Исключение в конструкторе + финализатор
От: _FRED_ Черногория
Дата: 08.07.10 11:44
Оценка:
Здравствуйте, Lazy Cjow Rhrr, Вы писали:

LCR>>>За возможность вызвать виртуальный метод в конструкторе — моя личная благодарность создателям и особенно тов. Хейлсбергу


_FR>>Тут вот я не согласен: оченьо неожиданные спец-эффекты бывают Нафик-нафик такие пироги. Ничего хорошего, "рождённого" из этой фичи, я ещё не видел и представить даже не могу. Может, покажешь?


LCR>Да, конечно, без проблем.


И неужель это понятнее работает, чем такое вот:

public class Parent
{
    protected readonly IScope Scope;

    public Parent() : this(new SimpleScope()) { }

    protected Parent(IScope scope)
    {
        Scope = scope;
    }

    public virtual void UseMe()
    {
        Scope.Wrap(() =>
        {
            // empty
        });
    }
}

public class Child1 : Parent
{
    public Child1() : this(new TransactionalScope()) { }

    protected Child1(IScope scope) : base(scope) { }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is inside a transaction
            // ...
        });
    }
}

public class Child2 : Parent
{
    public Child2() : this(new RepeatedScope(3)) { }

    protected Child2(IScope scope) : base(scope) { }

    public override void UseMe()
    {
        Scope.Wrap(() =>
        {
            // the stuff in UseMe is repeated 3 times
            // and throw an exception in case of failure
            // ...
        });
    }
}


LCR>В моём случае у меня были требования — класс должен иметь конструктор по-умолчанию.


Каждый класс и у меня имеет открытый конструктор по-умолчанию. Если требование распространялось и на не открытые конструкторы — то требования, мягко говоря, не традиционные.

LCR>Кроме того, оказалось гораздо удобнее позволить классу-наследнику Parent самому конфигурить себя, вместо того чтобы городить ioc-stuff.


Это что такое имеется в виду?

LCR>Без виртуальности в конструкторе вызов new Child1() создавал бы класс, но он бы пребывал бы какое-то время в невалидном состоянии (пока ему, скажем, не сделают Scope=..), что вряд ли бы добавило радости впоследствии.


Этого избежать удалось.
Help will always be given at Hogwarts to those who ask for it.
Re[9]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 11:50
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Тем, что в таком случае класс должен позволять все необходимые параметры передавать через конструктор, потому что иначе пользоваться таким классом без наследования будет нельзя. Если позволяет — то вызов виртуального метода не является необходимым. Если не позволяет — значит класс должен быть абстрактным (а в дельфях, как ты же и говорил, абстрактных классов нет ).


Ну опять же, в 99% перекрывать CreateParams не надо. Но есть еще 1%, когда не сложно и перекрыть метод. Тем более, что на каждую форму автоматически создается новый класс, так что перекрыть CreateParams не так уж и сложно. Ну а новый компонент и есть новый класс.

_FR>А тебе доподлинно известно, что CreateWindow происходит именно в конструкторе?


По крайней мере, если перекрыть CreateParams, то при вызове конструктора она дергается. А уж конструктор там, или виртуальные методы AfterConstruction срабатывают я не в курсе Вообще конструктор формы лезет в ресурсы, читает оттуда значения, которые прописаны в DFM, устанавливает их, вызывает Loaded, регистрирует класс окна, создает окно. Там же дергается OnCreate всякие и т. п.
Re[13]: Исключение в конструкторе + финализатор
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 08.07.10 12:09
Оценка:
Здравствуйте, _FRED_, Вы писали:

M>>А мне нужен именно CreateFile. Мне надо устанавливать всякие флаги, начиная от SecurityAttributes и заканчивая FILE_FLAG_NO_BUFFERING, FILE_FLAG_OVERLAPPED и т. п. В конце концов это может быть любой другой вызов API.


_FR>И что тебе мешает сначала сделать нормальную обёртку а потом уже нормально пользоваться ей вместо того что бы использовать как есть и говорить "как неудобно!"


Собственно говоря, напрягает необходимость делать такую обвертку. Потом напрягает читать разбухший код (везде добавлено .handle или ->handle), где есть обвертки над известными и понятными, давно обсосанными API-методами, да еще, возможно, со своими хитрыми нюансами. Опять же, создание такой обвертки требует определенного уровня знания всяких нюансов, о которых бы хотелось не касаться вовсе. Например, вопросы копирования. Дъявол подталкивает написать что-то универсальное, откуда возникает сложности на пустом месте. Это же может послужить источником ошибки. Плюс при отладке надо будет смотреть внутрь обверточного класса класса, ибо сразу хрен разберешь что там. Или писать еще скрипты. Короче, лишний геморрой на ровном месте.

Вообще, тут намного проще и приятнее сделать конструктор, который бы инициализировал все нулями, потом сделать метод init, который бы мог нормально проинициализировать объект, потом сделать метод, который бы последовательно создал объект, а потом вызвал init, и вернул что получилось. А в случае исключения вызвал, как и полагается, деструктор. Или дергать init ручками. Не смертельно, конечно.
Re: Исключение в конструкторе + финализатор
От: TK Лес кывт.рф
Дата: 08.07.10 13:33
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.


Потому, что конструктор не занимается созданием объектов. Он занимается лишь их инициализацией. Никто не запрещает создавать экземпляры без вызова конструктора и "инициализировать" их как-то еще...

Т.е. никого нарушения инварианта нет — финализатор вызывается для созданных объектов. Вот, если во время создания объекта возникнет OutOfMemoryException или выключат свет то, в этом случае, финализатор вызван не будет.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[7]: Исключение в конструкторе + финализатор
От: Lazy Cjow Rhrr Россия lj://_lcr_
Дата: 09.07.10 06:30
Оценка:
_FRED_,

_FR>И неужель это понятнее работает, чем такое вот:


_FR>
_FR>public class Parent
_FR>{
_FR>    protected readonly IScope Scope;

_FR>    public Parent() : this(new SimpleScope()) { }

_FR>    protected Parent(IScope scope)
_FR>    {
_FR>        Scope = scope;
_FR>    }
_FR>}
_FR>


Какой ты хитрый, отрефакторил таки. Придётся доставать говномёт (то есть копипасту (слегка причёсанную) из реального гов кода):
public class ParentElement
{
    private int checksum;

    public Parent()
    {
        checksum = CelculateCheckSum();
    }

    protected int CalculateCheckSum()
    {
        int result = 0;
        foreach (IElement cur: Elements)
        {
            if (!IsExcluded(cur.tag()))
                result += cur.CalculateCheckSum();
        }
        return result % 256;
    }
    
    protected virtual IEnumerable<IElement> Elements { get { /*some defaults*/ } }
    protected virtual bool IsExcluded(Tag) { return false; }
}


Два:
public class ParentWorker
{
    public Parent()
    {
        PrepareWorkers();
    }

    protected virtual void PrepareWorkers()
    {
        if (GlobalQueue.Get() != null)
        {
            // do nothing in this case, but 
            // derived classes will do some things like
            // worker = new Thread("DerivedWorker");
        }
        GlobalQueue.Put(this, "I am created!");
    }
}


Три:
public class CachedParent
{
    public Parent(ArrayList bytes)
    {
        // this call requires that Equals and GetHashCode
        // are defined correctly!
        GlobalCache.Add(this);
    }
    public override bool Equals(object obj)
    {
        if (object.ReferenceEquals(this, obj))
            return true;
        // bla-bla-bla
        return true;
    }
    
    public override int GetHashCode()
    {
        return 0; // please, override me
    }
}

Жирным помечены вызовы, чувствительные к виртуальному вызову. Разумеется, workaround всегда есть, дублирование там, отдельный метод Init. В C++ народ же как-то обходится, живёт с этим и не жужжит (вроде бы).

LCR>>Кроме того, оказалось гораздо удобнее позволить классу-наследнику Parent самому конфигурить себя, вместо того чтобы городить ioc-stuff.

_FR>Это что такое имеется в виду?
Inversion-of-control-хрень
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[11]: Исключение в конструкторе + финализатор
От: Jack128  
Дата: 09.07.10 08:54
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, Jack128, Вы писали:


0>При попытке динамически вызвать конструктор вылетит птич... исключение


В ран тайм... Ну так в ран тайм и в дельфи поднимается исключение. При вызове абстрактного метода..
Re[12]: Исключение в конструкторе + финализатор
От: 0x7be СССР  
Дата: 09.07.10 08:59
Оценка:
Здравствуйте, Jack128, Вы писали:

0>>При попытке динамически вызвать конструктор вылетит птич... исключение

J>В ран тайм... Ну так в ран тайм и в дельфи поднимается исключение. При вызове абстрактного метода..
Тут есть принципиальная разница. Обращения к абстрактному методу можно исключить на этапе компиляции. А попытку создать объект абстрактного класса через ссылку на метакласс — нельзя. Хотя, если подумать, то вполне можно
Re: Исключение в конструкторе + финализатор
От: rm822 Россия  
Дата: 16.07.10 22:00
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Коллеги!


0>Мне одному кажется неправильным, что при вылете исключения из конструктора класса для недосозданного объекта вызывается финализатор того же класса? Получается, что мы имеем нарушение инварианта — происходит вызов метода объекта, для которого не отработал конструктор.


в дотнете файналайз это просто метод вызываемый ГЦ — ну поленились разрабы, чего тут непонятного?
конечно можно(и нужно,я считаю) было сделать как ты хочешь
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.