Entry tags:
Западло
[ Предупреждение: для непрограммистов неинтересно ]
Пусть у нас есть два класса - базовый Base и отнаследованный от него Derived. В Derived есть конструктор, похожий на конструктор копии с параметром типа Base.
Допустим, в программе это используется вот так:
И оно не работает.
Тщательное расследование показало эпическое западло...
В какой-то момент я внес часть кода в функцию:
И вдруг оно начало работать! Счастливый, я все так и оставил, предполагая разобраться как-нибудь потом. Сегодня это как-нибудь наступило.
Западло состоит в том, что в строке Derived d2(d1) не всегда вызывается второй конструктор и не будут выполняться "важные манипуляции". Вместо этого будет вызван "невидимый" автоматически сгенерированный конструктор-копии, и все поля будут тупо скопированы из d1 в d2.
Это происходит в том случае, если d1 имеет тип Derived. Если Base - то все хорошо. Вот почему та функция исправляла проблему. Так тоже будет работать:
Лечится это легко:
Но ведь и вляпаться тоже легко...
Пусть у нас есть два класса - базовый Base и отнаследованный от него Derived. В Derived есть конструктор, похожий на конструктор копии с параметром типа Base.
class Base { ... }; class Derived: public Base { public: Derived() {...} Derived(const Base &b) { /* важные манипуляции */ } };
Допустим, в программе это используется вот так:
Derived d1; Derived d2(d1);
И оно не работает.
Тщательное расследование показало эпическое западло...
В какой-то момент я внес часть кода в функцию:
void foo(Base &d1) { Derived d2(d1); ... } ... Derived d1; foo(d1);
И вдруг оно начало работать! Счастливый, я все так и оставил, предполагая разобраться как-нибудь потом. Сегодня это как-нибудь наступило.
Западло состоит в том, что в строке Derived d2(d1) не всегда вызывается второй конструктор и не будут выполняться "важные манипуляции". Вместо этого будет вызван "невидимый" автоматически сгенерированный конструктор-копии, и все поля будут тупо скопированы из d1 в d2.
Это происходит в том случае, если d1 имеет тип Derived. Если Base - то все хорошо. Вот почему та функция исправляла проблему. Так тоже будет работать:
Derived d2((Base&)d1);
Лечится это легко:
class Base { ... }; class Derived: public Base { public: Derived() {...} Derived(const Base &b) { /* важные манипуляции */ } Derived(const Derived &b) { /* важные манипуляции */ } };
Но ведь и вляпаться тоже легко...
no subject
Сейчас не удалось воспроизвести ситуацию с ошибкой из-за отсутствия типизациии у макросов, с которой бодался я пару лет назад. В любом случае она снимается конструкцией вида
#define CH ((char)'0')
но об этом мало кто помнит.
>И повторю еще раз: в любом случае enum лучше.
Я не говорил о том, что enum хуже. Использование enum для определения констант рекомендуется ведущими
собаководамиспециалистами наравне с const, но никак не с макросами.>У тебя получается, что куча времени на отлов потенциальной ошибки существует, а куча времени на набивание - не существует?
Если есть возможность отловить/пресечь потенциальную ошибку компилятором, то эту возможность нужно использовать. Это одно из фундаментальных правил. Если так нравится натыкаться на грабли - флаг в руки.
У меня константы в классе описываются крайне редко. Потому что константа на то и константа, что она одинакова для всех instance и может быть оформлена в качестве константы пространства имен или геттера.
>Это уже опять пошла религия. Думаешь, сказал слово "хак" и на белого коня влез?
Покажи
справкустандарт языка, где такой префикс обязателен для всех компиляторов. Жду.Слово "хак" означает вполне конкретное действие, которое направлено на использование особенностей текущей системы или поведения программы для достижения локальной цели, но при этом не соответствует нормам и ведет к большой жопе в долгосрочной перспективе.
Если программист для ожидания ввода пишет
std:cin >> "";
то это грязный хак.
Если у него программа при int x[100] не работает по каким-то непонятным причинам, а при исправлении на int x[1000] все вдруг работает, и при этом нет никакого анализа причин - то это также грязный хак.
В нормальных фирмах за грязные хаки увольняют без разговоров. Потому что никому не нужны 10% программистов делающих 90% ошибок в проектах.
Я понятно объяснил, что такое "хак"?
>Именно так и надо писать глобальные #define-ы, тогда и не придется тратить "куеву хучу времени" на отлов ошибок из-за повторения констант.
Способов написания "уникальных" define'ов много, и я их тут не затрагиваю.
no subject
[ Сейчас не удалось воспроизвести ситуацию с ошибкой из-за отсутствия типизациии у макросов ]
При желании все можно придумать - просто это редкие ситуации. При желании можно и
const int string= "";
написать, а потом удивляться, что за фигня получается, когда путается с std::string.
[ Использование enum для определения констант рекомендуется ведущими собаководами специалистами наравне с const, но никак не с макросами. ]
Ну вот потому мне тот собаковод и не понравился, что уперся в const :)
[ Если есть возможность отловить/пресечь потенциальную ошибку компилятором, то эту возможность нужно использовать ]
Так и с #define-ом есть возможность пресечь - используй достаточно уникальные имена, а не "CH" и не "WIDTH1". :)
[ Покажи справку стандарт языка, где такой префикс обязателен для всех компиляторов. ]
При чем тут компиляторы? Это просто традиция - облегчать себе жизнь префиксами. Скажем, в виндах пишем не TIMER, а WM_TIMER, где префикс WM_ - есть аббревиатура от Window Message. Та или иная схема префиксов должна быть.
[ Слово "хак" означает вполне конкретное действие, которое направлено на использование особенностей текущей системы или поведения программы для достижения локальной цели, но при этом не соответствует нормам и ведет к большой жопе в долгосрочной перспективе. ]
Ну так слезай с белой лошадки, Всякие WM_ или Class__ под это определение не подходят и, следовательно, хаками не являются. :)
no subject
Я кажется уже написал, что попадал в такую 'редкую' ситуацию.
>Ну вот потому мне тот собаковод и не понравился, что уперся в const :)
Всякому овобщу свое время.
>Так и с #define-ом есть возможность пресечь - используй достаточно уникальные имена, а не "CH" и не "WIDTH1". :)
См. примеры 3.3, 3.4. Ошибки компилятором не выявлены.
Опять же, уникальность ещё можно обеспечить какими либо средствами, но вот контроль компилятора - нет.
>При чем тут компиляторы? Это просто традиция - облегчать себе жизнь префиксами. Скажем, в виндах пишем не TIMER, а WM_TIMER, где префикс WM_ - есть аббревиатура от Window Message. Та или иная схема префиксов должна быть.
Это просто префикс, который не делает так, что константа попадает в пространство имен?
namespace используется не только для уникальности. например это удобная вещь при рефакторинге. Если есть класс A::B::C, и его нужно перенести в E::F::D, то копируется он сам как таковой без каких либо правок, просто включается в другое пространство имен, при этом другой код может его использовать точно также, как использовал ранее. И не нужно бегать по всему проекту и по всем пользователям, использующим код для произведения правок.
>Ну так слезай с белой лошадки, Всякие WM_ или Class__ под это определение не подходят и, следовательно, хаками не являются. :)
На вопрос ответь сначала (;
no subject
Ну и на здоровье :))))
[ Всякому овобщу свое время. ]
Нет, там явно говорится, что enum - устаревшая, экзотическая техника. Практически "хак по Бсивко" ;))))
[ См. примеры 3.3, 3.4. Ошибки компилятором не выявлены. ]
3.4 - вообще не константа, не собираюсь защищать define-ы в качестве альтернативы inline-функций. 3.3 - #define с операциями и без внешних скобок пишут только индийские программисты. :)
[ Это просто префикс, который не делает так, что константа попадает в пространство имен? ]
Ну он просто дает аналогичный эффект - гарантирует, что не будет одноименных констант. Хотя компилятор обычно и так ругается на повторный #define, но так лучше (думаю, понятно, почему).
[ На вопрос ответь сначала (; ]
На который??
no subject
Ну и на здоровье :))))
---------
Это говорит о том, что ситуация не такая уж и редкая.
---------
Ну он просто дает аналогичный эффект - гарантирует, что не будет одноименных констант. Хотя компилятор обычно и так ругается на повторный #define, но так лучше (думаю, понятно, почему).
[ На вопрос ответь сначала (; ]
На который??
---------
Уже ответил.
И я тебе уже тоже ответил. Namespace это не только
30 кг легко усваиваемого мясаспособ создания уникальных имен, но и например удобный элемент при рефакторинге.---------
[ Всякому овобщу свое время. ]
Нет, там явно говорится, что enum - устаревшая, экзотическая техника. Практически "хак по Бсивко" ;))))
---------
>Т.е. это описывается как "трюк", который надо знать для каких-то изъебских случаев - в то время, как это не трюк, а метод, который лучше того метода, который перед этим рекламируется.
Enum не предназначен для описания констант. Поэтому это именно трюк, но не метод в рамках языка.
Т.о. Майерс его не рекомендует, Страуструп и Саттер рекомендуют. Программистам же остается включать мозги и делать выбор. У тебя с этим проблемы нервного плана? Раньше за тобой такого не замечал.
Но то, что хотел сказать, не в этом. А в том, что по твоим словам (2009-07-14 05:42 pm)
Использование const вместо #define соответсвует чисто идеологическому переходу с парадигмы C на C++. Соответственно везде, где пропагандируется идеология C++, пропагандирутся и const вместо #define. Проблема в том, что это чисто идеологическая, пропагандистская фишка. Ее назначение - разного рода холивары и пособия типа "C++ за два дня для ламеров".
Для ламера надо вызубрить некое правило, как можно более простое и бездумное. Например, "препроцессор - плохо, const - хорошо". Без всяких оговорок типа, когда, где и при каких условиях. Если же подходить с практических позиций, то лучше далеко не всегда и не везде. Но это надо чуть-чуть мозги включать и вникать.
-- конец цитаты --
в книге - набор тупых правил для чайников. А на самом деле в книге подробно расписывается, что как и почему. В частности, для enum (там же):
"Этот прием стоит знать по нескольким причинам. Во-первых, поведение "трюка с перечислением" в некоторых отношениях более похоже на #define, чем на константу, а иногда это как раз то, что нужно. Если вы хотите запретить получать адрес или ссылку на какую-нибудь целую константу, то применение enum - хороший способ наложить такое ограничение."
(есс-но define-аналогии в сторону, важны сами факты различных способов)
т.е. конкретно расписаны плюсы и минусы (и это только часть, в книге ещё есть), и при этом рекомендуется использовать константу, т.к. это дает больше возможностей программисту и оптимизации компилятору.
Ламеры будут использовать константу - обойдут потенциальные грабли и получат большую функциональность. Те, кто понимают что делают, будут использовать enum там, где это оправдано. Так что все корректно и к месту.
>И нехуй мне тут пиздить, что я хуем по заголовкам читаю.
Так что пиздить мне тут хуй (;
Все ещё настаиваешь на обвинении Майерса в пособии необоснованных правил для чайников?