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
Знаю таких "оптимутмистов". После которых проектный код проще просто выкинуть и написать нормально. Уже не в первый раз сталкиваюсь и не только на С++. Потому что вылетают ошибки из ниоткуда, память протекает абы как и приложение держит нагрузку в единицы транзакций в секунду вместо потенциальных сотен и тысяч. Это разница между экономичным и профессиональным кодом. Когда проект делается не впопыхах к вчера, а с соблюдением всех фаз и проверок на всех этапах.
Что ты хотел сказать про функциональность?
>.. А сложные названия с уникальными префиксами можно переименовать за пару секунд в автоматическом режиме find/replace.
Ещё раз: find/replace для внешних сущностей делается и там, и там. Для внутренних он необходим для define, для namespace он уже не нужен.
Разница define и namespace - именно в том, что define - это препроцессор, не относящийся к языку никаким боком. namespace - это языковое средство, которое контролирует, использует и понимает уже не только программист, но и компилятор.
И различных особенностей и преимуществ namespace много. Например, подключили библиотеку X и библиотеку Y, и оказалось, что в них конфликт имен Z. Для namespace проблема решается легко:
namespace X {
#include X
}
namespace Y {
#include Y
}
X::Z
Y::Z
что же ты будешь делать с #define?
Или например namespace имеют свойство вложенности. Когда текущий проект можно моментально сделать подпроектом одним namespace.
Я не специалист по холиварам и всех особенности и того и другого не назову. Для этого есть умные книжки и справочники. То, что в С++ систематически убивается препроцессор, ясно и понятно уже давно. Есс-но этому сопротивляется С-ишное старье и привычки. Отношение создателя С++ к препроцессору очень хорошо показано в главе "18. Препроцессор С" книги "Дизайн и эволюция С++" Страуструпа (с примерами, особенностями и инструментами замены). И это не идеология. А естественный процесс отмирания атавизма.
>Пусть есть оператор a += c, где c - числовая константа. Компилятор может преобразовать это в ассемблерную команду, где константа лежит с сегменте кода, составляя часть команды (add или fadd).
>А также компилятор может завести переменную в сегменте данных и в ассемблерной команде сослаться на нее.
Ключевой момент в том, что компилятор может. Он знает, что сущность - константа, а после define'а он может об этом только догадываться. Поэтому в случае константы у компилятора есть выбор и он может с'опитимизировать работу программы как в сторону экономии памяти, так и в сторону ускорения работы.
>А к вопросу о скорости, значит, тебя таки несло без пургена?
const std::string c_str("ABC");
#define C_STR std::string("ABC")
В каждом define сработает конструктор. В const объект создается один раз.
И это оченьвидный пример. Но главное, что компилятор знает, что константа - это одинаковая неизменяемая сущность во многих кусках кода, а define для него - это фигня, разбросанная по всему коду, о сущности которой можно только догадываться. Поэтому основное правило заключается в том, что чем больше компилятор знает о коде, тем об лучше сможет его с'оптимизировать.
>Но! Все, что я написал, относится и к const, и к define, и к enum. Для всех трех случаев компилятор может использовать и первый способ, и второй.
"Шушпанчики могут работать 365 дней в году. Но не работают." (с)
>При этом есть одна неприятность: команда должна обращаться и к сегменту кода, и к сегменту данных, что теоретически ведет к замедлению.
В современных процессорах необходимые данные на последующую команду загружаются для последующей обработки заранее. Аппаратная эволюция не стоит на месте.
>Я не обсуждал производительность и память по той простой причине, что эти факторы в данном конкретном случае не влияют. В другом случае - влияют, и в другом случае у меня были претензии к афтару как раз в плане производительности.
Как оказывается - влияют. Потенциально не всегда, но - влияют. И чем умнее и совершеннее компилятор - тем больше.
no subject
А дальше пошел левый пиздёж - наезд, ничем не обоснованный. В таком стиле я разговаривать не буду, времени жалко, пошел нахуй.
[ const std::string c_str("ABC"); ]
Речь шла о числовых константах, у тебя склероз что ле?
[ В современных процессорах необходимые данные на последующую команду загружаются для последующей обработки заранее. ]
Америку, блядь, открыл...
[ Но главное, что компилятор знает, что константа - это одинаковая неизменяемая сущность во многих кусках кода, а define для него - это фигня, разбросанная по всему коду, о сущности которой можно только догадываться ]
Для компилятора нет define, препроцессор уже подставил вместо имени define число. А одно и то же число - это сущность неизменяемая. Короче, не строй из себя идиота.
no subject
>Речь шла о числовых константах, у тебя склероз что ле?
Нет, я работаю с первоисточником. У Мейерса нет ни слова про числовые константы, их похоже выдумал ты.
В любом случае если имеются какие-либо действия по конструированию элемента, они произойдут при использовании define. При const этого нет.
Придумай себе класс числовых значений (например комплексных/рациональных или ещё каких чисел) и получишь соответствующий результат.
>Америку, блядь, открыл...
Для тебя похоже что открыл. Т.к. ты рассуждаешь тут теоретически, а на самом деле во многом все зависит и от компилятора, и от харда. И "в среднем" специалисты говорят, что const позволяет работать быстрее и использовать меньше памяти.
Так что не строй из себя идиота.
Неудобные вопросы ты успешно проигнорил. Недостаток квалификации в предмете и дискуссии "компенсируется" квалификацией в полемике.
>Для компилятора нет define, препроцессор уже подставил вместо имени define число. А одно и то же число - это сущность неизменяемая. Короче, не строй из себя идиота
Это число ещё нужно найти и сопоставить его в различных кусках кода. Может ещё скажешь в каждом "for( int i =0; ..." компилятор где-то 0 хранит как одинаковое число? Фигвам.
no subject
Я ссылался на первоисточник, на страницы 29-30. Страница 29, читаем:
const double CostEstimate::FudgeFactor = 1.35;
Это не числовая константа?
Мое терпение иссякло. Пошел на хуй. За такое уебство я не баню, но общаться с таким говном вряд ли еще стану.
no subject
Всего хорошего.
P.S.
— Вас не затруднит? Будьте любезны, передайте на билетик, пожалуйста! Спасибо.
— Ну ты, хрен, из интеллигентов, что ли?
— Да нет, что вы, я такое же быдло, как и вы!
(с) Вежливо
no subject
no subject
Знал бы такую реакцию, в спор бы не вступал. Здоровье (не только мое) оно того... дороже.
Сейчас ещё раз меня пошлют на всякий случай, и все будет нормально (;
no subject