psilogic: (Default)
psilogic ([personal profile] psilogic) wrote2009-07-14 04:31 pm

Западло

[ Предупреждение: для непрограммистов неинтересно ]

Пусть у нас есть два класса - базовый 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) { /* важные манипуляции */ }
};


Но ведь и вляпаться тоже легко...

[identity profile] seryjvolk.livejournal.com 2009-07-14 01:39 pm (UTC)(link)
Скотт Мейерс. Кажется "50 рекомендаций...".
Очень полезная книжка, нагуглите.

[identity profile] psilogic.livejournal.com 2009-07-14 01:46 pm (UTC)(link)
Предлагаете прочитать 240 страниц, чтобы найти 2 новые рекомендации среди 48 давно знакомых? :)

[identity profile] seryjvolk.livejournal.com 2009-07-14 02:30 pm (UTC)(link)
Я не хотел вызывать вашего раздражения.
Думаю, иногда 240 стр. не слишком высокая цена за новые знания.

[identity profile] psilogic.livejournal.com 2009-07-14 02:46 pm (UTC)(link)
В данном случае неприемлемо соотношение цена/качество :)

[identity profile] psilogic.livejournal.com 2009-07-14 01:57 pm (UTC)(link)
Ради интереса скачал, посмотрел начало. Качество книжки низкое. Сходу наткнулся на пару вредных советов, выдаваемых как откровения.

[identity profile] --ronin--.livejournal.com 2009-07-14 02:30 pm (UTC)(link)
Каких именно?

[identity profile] psilogic.livejournal.com 2009-07-14 02:49 pm (UTC)(link)
Использование const вместо #define для числовых констант.

Использование ссылок в inline-функции, которая в 99% случаев будет работать с простыми типами.

Еще что-то... ах да, идеологических сентенций типа "расширяемость С++ это круто, поэтому давайте все время ориентироваться на нее".

[identity profile] ab83.livejournal.com 2009-07-14 04:40 pm (UTC)(link)
Извините, что встреваю, а нельзя ли про вредность вот этого
"Использование const вместо #define для числовых констант"
чуть подробнее.

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

[identity profile] psilogic.livejournal.com 2009-07-14 05:42 pm (UTC)(link)
Использование const вместо #define соответсвует чисто идеологическому переходу с парадигмы C на C++. Соответственно везде, где пропагандируется идеология C++, пропагандирутся и const вместо #define. Проблема в том, что это чисто идеологическая, пропагандистская фишка. Ее назначение - разного рода холивары и пособия типа "C++ за два дня для ламеров".

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

Конкретно в отношении const.

1. Для автоматических переменных получается переменная, ограниченная по области видимости. Это бонус, отмечаем его как "+1". Плата за это небольшая - чуть больше писать:

#define MAX_CHANNEL 2
и
const int MAX_CHANNEL = 2;

Поскольку писать надо больше совсем чуть-чуть, можно отметить это не как "-1", а, скажем, "-0.5". Итого: +1 против -0.5. Вывод - для автоматических переменных const немного лучше.

2. Для глобальных переменных у const пропадает бонус ограниченной области видимости. Зато может появиться другой бонус: в отладчике видно ее значение, а значение #define не видно. Но это зависит от отладчика. Иной отладчик и #define видит. Так что получается "+0.5". Далее, начинаются несовместимости между C и C++, разные тонкости с версиями компиляторов. Но это тоже редкое явление, так что тоже "-0.5". И еще "-0.5" за то, что длинее.

Итого: +0.5 против -1. Вывод - для глобальных переменных const немного хуже.

3. Если числовая константа - член класса, то для корректного оформления в виде const придется вносить правку и в хэдер, и в исходник, количество писанины растет:

.h:
static const int MAX_CHANNEL = 2;
.cpp:
const int ClassName::MAX_CHANNEL;

Это уже -1. При написании кода появляется необходимость переключаться между файлами. Еще -1. За одно и то же время можно написать константу через const, либо константу через #define плюс комментарий к ней.

Итого: +1 против -2. Вывод - для констант класса const уже намного хуже.

Но самое прикольное то, что есть enum, который лучше, чем #define и лучше, чем const в случаях 2 и 3. Он и показывается в отладчике, и позволяет ограничивать область видимости классом, и не требует залезать в .cpp, и писанины не больше:
enum { MAX_CHANNEL = 2 };

Но enum - "не идеологичен". ;)

Вывод не для ламеров: для автоматических констант юзать const, для глобальных констант (в том числе констант класса) юзать enum.

[identity profile] alisarin.livejournal.com 2009-07-15 06:18 am (UTC)(link)
Не понимаю о чем это, но смеюсь над тем _как_это_ :))

[identity profile] psilogic.livejournal.com 2009-07-15 07:47 am (UTC)(link)
Интересно, что именно показалось зобавным? :)

(no subject)

[identity profile] alisarin.livejournal.com - 2009-07-15 08:52 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-15 09:13 (UTC) - Expand

(no subject)

[identity profile] alisarin.livejournal.com - 2009-07-15 09:31 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-15 09:37 (UTC) - Expand

(no subject)

[identity profile] alisarin.livejournal.com - 2009-07-15 09:43 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-15 09:45 (UTC) - Expand

(no subject)

[identity profile] alisarin.livejournal.com - 2009-07-15 09:52 (UTC) - Expand

[identity profile] bsivko.livejournal.com 2009-07-15 12:19 pm (UTC)(link)
1. Для автоматических переменных получается переменная, ограниченная по области видимости. Это бонус, отмечаем его как "+1". Плата за это небольшая - чуть больше писать:

#define MAX_CHANNEL 2
и
const int MAX_CHANNEL = 2;

Поскольку писать надо больше совсем чуть-чуть, можно отметить это не как "-1", а, скажем, "-0.5". Итого: +1 против -0.5. Вывод - для автоматических переменных const немного лучше.
------
Писать больше только для типизации. Если она нужна (а она часто именно нужна, хотя обезьяне с гранатой она обычно не нужна; а в макросах возможности типизации(отобрать гранату) нет).
Если программист привык писать наобум, то держи достойную замену:
const MAX_CHANNEL = 2; // без int, все работает

Ограничение области видимости - это возможность. При желании const можно засунуть в namespace, define же лишены этой привилегии и могут разнести в пух и прах кучу любого подключенного кода, и хорошо если это компилятор заметит. const'ы же избавляют от множества потенциальных ошибок данного рода, за что им в данной шкале +10. Обычно это понимают только после наступленияы
------------

2. Для глобальных переменных у const пропадает бонус ограниченной области видимости.
------------
Глобальное пространство имен лучше вообще не засорять. За всю историю использования плюсов в боевых проектах мне приходилось использовать глобальные константы только в единичных случаях пару раз.

------------
3. Если числовая константа - член класса, то для корректного оформления в виде const придется вносить правку и в хэдер, и в исходник, количество писанины растет:

.h:
static const int MAX_CHANNEL = 2;
.cpp:
const int ClassName::MAX_CHANNEL;

Это уже -1. При написании кода появляется необходимость переключаться между файлами. Еще -1. За одно и то же время можно написать константу через const, либо константу через #define плюс комментарий к ней.

Итого: +1 против -2. Вывод - для констант класса const уже намного хуже.
--------------
Тут вообще глупость написана. Потому что define не может быть членом класса. Потому он не имеет никакого преимущества. Возможность прописать const в класс является дополнительной возможностью, которую можно использовать по своему усмотрению, что несомненно '+'.

--------------
В свое время мой вывод не для ламеров - везде где можно заменить константу define на const - нужно это делать, и при этом провести типизацию.
define же использовать для хитрожопых конструкций замены, экзотических случаев или случаев, когда этого требует комплиятор/библиотека или ещё кто.

[identity profile] bsivko.livejournal.com 2009-07-15 12:21 pm (UTC)(link)
*Обычно это понимают только после наступленияы

недописано (;

после наступления на грабли и очухивания и обхода их в течении часов или даже дней глубокой отладки.

[identity profile] psilogic.livejournal.com 2009-07-15 12:35 pm (UTC)(link)
[ Писать больше только для типизации. ]

Вот это как раз - "достойный" пример бездумного следования религиозной заповеди: "типизация - это хорошо" - всегда, независимо ни от чего. А теперь просвети меня, как и почему в данном конкретном случае типизация - это хорошо? Не в параметрах функций, а вот конкретно для констант. Слабо обосновать? Ну и для закрепления пройденного, подумай: что, у констант типов нет? Суффиксы L, U, F не для эстетов? :)

[ Ограничение области видимости - это возможность. ]

Ты вообще читаешь то, что комментируешь? Я отметил, что "это бонус" - какие претензии, не понимаю.

[ за что им в данной шкале +10 ]

А вот это - уже религиозно-идеологическое воззрение. Для холиваров - прекрасно, для серьезного рассмотрения - негодно. Может, сразу +1000^1000? :) Ну чиста патаму шта эта крута!? :)

[ Потому что define не может быть членом класса. ]

Следи за руками:
#define ClassName__MAX_CHANNEL = 2;
:)

Ну и про enum не забудь, он все равно лучше.

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-15 12:46 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-15 12:48 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-15 13:10 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-16 20:00 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-16 22:13 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-16 22:26 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-16 22:42 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-17 14:07 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-16 20:00 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-16 22:22 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-16 22:54 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-16 23:36 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-17 19:06 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-17 19:57 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-17 20:19 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-17 20:48 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-17 23:16 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-18 08:11 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-19 02:29 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-19 09:12 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-19 10:46 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-19 10:58 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-19 11:49 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-19 11:52 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-19 13:04 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-19 13:19 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-19 02:29 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-19 09:05 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-19 10:48 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-19 10:54 (UTC) - Expand

(no subject)

[identity profile] bsivko.livejournal.com - 2009-07-17 23:16 (UTC) - Expand

[identity profile] ab83.livejournal.com 2009-07-15 02:21 pm (UTC)(link)
Спасибо.

И эта... Я не хотел, чтобы Вы втянулись в спор. Извините, если что.

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-15 15:41 (UTC) - Expand

[identity profile] nefedor.livejournal.com 2009-07-21 02:44 am (UTC)(link)
Так получилось, что я совсем недавно смотрел упомянутого мейерса, причем не первое издание древнего года, а третье и пока последнее, кажется, 2005го.
Так вот, пару слов.
Во-первых, про enum. Как правильно заметил бсивко, мейерс именно что активно рекомендует использовать enum. Он просто говорит, что в программистских кругах сложилось название для подобного использования enum-а - "enum hack". Но из текста (английского по крайней мере) совершенно никак не следует, что это нечто грязное и нечистоплотное, наоборот, этот "хак" преподносится как нечто, что следует знать и использовать.
Во-вторых, про пункт 3, то есть, про константы - члены класса. Тот же мейерс в той же главе пишет, что для типов int, char и bool объявлять переменную в .cpp не нужно - достаточно декларации в хедере, что является стандартом языка. Правда, некоторые старые компиляторы этого могут не поддерживать, но более-менее новые должны. Поэтому писанины становится меньше в два раза.
Ну и в-третьих, пустые холивары всегда бывают от абсолютизации чего-то. Того же мейерса делать непогрешимым кумиром вряд ли стоит. С другой стороны, также вряд ли стоит утверждать его ламерство, в то время как он является распространенным и уважаемым автором для многих программистских коллективов. Скажем, ты можешь не разделять их и бсивко воззрения на минимизацию времени на отладку и поиск багов, но они в свою очередь, могут не разделить твое видение себя в их рядах в случае поиска работы... :)

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-21 09:21 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2009-07-21 14:51 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-21 15:04 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2009-07-21 15:28 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-21 16:15 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2009-07-21 16:31 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-21 16:50 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2009-07-21 17:25 (UTC) - Expand

(no subject)

[identity profile] psilogic.livejournal.com - 2009-07-21 17:39 (UTC) - Expand

(no subject)

[identity profile] nefedor.livejournal.com - 2009-07-21 18:08 (UTC) - Expand

[identity profile] bsivko.livejournal.com 2009-07-15 11:55 am (UTC)(link)
Важные манипуляции объединить нужно в одну функцию.

[identity profile] psilogic.livejournal.com 2009-07-15 11:58 am (UTC)(link)
Нужно в одну функцию объединить их, согласен с вами я! :)

[identity profile] http://technorati.com/people/technorati/ketmar (from livejournal.com) 2009-07-19 06:58 am (UTC)(link)
э… а где западло-то? компилятор сделал точно то, о чём его попросили же. с какой стороны я не смотрел, но равенства «Base» и «Derived» в упор не увидел. более того — не увидел даже фигнюшки для преобразования одного в другое (тогда, емнип, компилер мог бы нашаманить преобразование и вызов конструктора, уж не помню, лень проверять, это идиотизм всё равно). а для случая «коструктор копирования отсутствует» стандарт прямо предписывает его неявно зашаманить, а не ебстись с преобразованием типов. именно поэтому конструктор копирования, если он не очевиден, всегда объявляют явно. а дальше пишут там *this = aValue (при наличии унаследованного — или своего — operator =, натурально).

итого: «а этот пацак всё время думает на языках, продолжения которых не знает!» (ц)

[identity profile] http://technorati.com/people/technorati/ketmar (from livejournal.com) 2009-07-19 07:08 am (UTC)(link)
тьфу, йопт. про преобразование — это ж и есть «второй конструктор», ага. %-)

[identity profile] psilogic.livejournal.com 2009-07-19 08:48 am (UTC)(link)
ну вот и получается, что смотришь и думаешь: конструктор копирования есть, все ништях. никто не забыт, ничто не забыто. а его нет :)

[identity profile] http://technorati.com/people/technorati/ketmar (from livejournal.com) 2009-07-19 01:30 pm (UTC)(link)
не, ну как, как можно подумать, что он есть, если сразу видно, что его нет нифига? O_O

ты ж, вроде бы, не первый год на цпп пишешь, вообще должен «влёт» такое замечать. если уж я, который цпп люто ненавидит и пишет в нём в 90% случаев c-style код, вижу…

это, по-моему, во всех книжках напрочь говорится почти сразу: «если явного конструктора копирования нет, компилятор нашаманит свой. везде и завсегда».

зыж херня это всё. (томно вздыхает) вот шаблоооны… «ах, швецiя!» (ц)

[identity profile] psilogic.livejournal.com 2009-07-19 02:18 pm (UTC)(link)
да это все понятно, просто сочетание всех факторов создает неочевидный глюк. ворнинг тут был бы пользителен.

а про Щаблоны хошь задачку прикольную? :)

[identity profile] http://technorati.com/people/technorati/ketmar (from livejournal.com) 2009-07-19 02:27 pm (UTC)(link)
неа, не хочу. я не умею делать шаблоны, только использовать готовые, у которых хорошая документация. я ж не Ъ-цпп-кодер. я вон до недавних пор не знал, что от шаблонизированного класса можно наследоваться (в смысле, например, от QList<int>).

[identity profile] psilogic.livejournal.com 2009-07-19 02:36 pm (UTC)(link)
ну если захочешь потренироваться то тута:
http://psilogic.livejournal.com/326274.html