Цитата(haker_fox @ Jun 22 2007, 08:58)

Здравствуйте! До недавнего времени я использовал только си. В настоящее время я стараюсь писать максимально на языке си++, правда мои старания пока ограничиваются классами и некоторыми другими возможностями этого языка. Суть моего вопроса: каким образом может сказаться использование только одних
#define, вместо рекомендуемых
const?
Просто для меня лично более понятно это:
Код
/* Valve's pin */
#define VLV_TURN_LEFT_PIN 0
...
#define VLV_CLOSE_PIN 7
Чем примерно такое:
Код
const int VLV_TURN_LEFT_PIN 0
и т.д.
В данном случае конечно можно использовать (как я правильно понял) enum.
Но мне нравятся дефайны. Может ли иметь это какие-либо неприятные последствия в будущем?
Заранее спасибо за ответы и сорри за ламерский вопрос...
Дефайн - и в С, и в С++ это просто тупой макрос, тупой в том смысле, что когда он встречается, то производится просто тупая макроподстановка безо всякого анализа того, что получается в результате. Это зачастую является источником труднообнаруживаемых багов - в исходном коде одно, а компилятор видит другое.
Константный же объект - это объект, обладающий типом, поэтому он прямо попадает под контроль типов компилятора, что делает его использование безопасным и однозначным. С точки зрения реализации констатны в С++ нечем не уступают препроцессорным макросам (дефайнам). Макросы по большому счету к месту использовать только для нужд условной компиляции.
Цитата(DRUID3 @ Jun 22 2007, 10:09)

А const int VLV_TURN_LEFT_PIN = 0; это переменная типа int с именем VLV_TURN_LEFT_PIN и равная "0" которую нельзя изменять. Она лежит в памяти (как во Flash так и в RAM во время исполнения).
Это в С она лежит в памяти. Из-за того, что у константных объектов в С по умолчанию внешнее связывание. А в С++ у константных объектов по умолчанию внутреннее связывание, что позволяет компилятору оптимизировать константу и не размещать ее в памяти - все приличные компиляторы сегодня это умеют. В памяти констатный объект обязан быть размещен только в двух случаях:
1. Явно указано внешнее связывание этого объекта - с помощью ключевого слова
extern.
2. В коде присутствует взятие адреса этого константного объекта.
В обоих этих случах компилятору ничего не остается, как разместить объект памяти. В других случаех это не не обязательно, чем и пользуется оптимизатор компилятора. Итого, использование того же
Код
// file.h
int a = 10;
...
void f(int x);
// file.cpp
...
f(a);
не приведет ни к какому размещению константы 'a' в памяти - будет точно такая же подстановка, как и в случае с макросом. Но уже с контролем типов.
Цитата(DRUID3 @ Jun 22 2007, 10:09)

А уж enum, да, это злобный агент C++. В "+ы" перечислениям ставят то, что при инициализации констант компилятор может выполнить проверку типов
О какой проверке типов идет речь?
Цитата(DRUID3 @ Jun 22 2007, 10:09)

(1). А еще, поскольку перечисления - это типы, определенные пользователем то для них можно перегрузить операции
Типами, определяемыми пользователем, в С++ являются классы и только они. Для них, действительно, можно перегрузить операции. Перечислымый тип enum не является типом, определяемым пользователем, и для него никакие операции перегрузить нельзя.
Единственным назначением перечислимых типов является ограничение принимаемых значений с возможностью задания им сивмолических имен. Перечислимый тип даже не вводит своего пространства имен (scope) и имена его значений видны в том пространстве имен, где он объявлен.
Кстати, перечислимый тип в нормальном виде присутствует только в С++ - он именно вводит перечень принимаемых значений. В С enum штука почти вообще безполезная - с объектами этого типа можно делать все, что угодно - присваивать им любые значения, производить арифметические действия. Словом, он по сути не отлчичается от обычных интегральных встроенных типов. В С++ такие финты не проходят - там область значений задана определением перечислимого типа, и пихнуть туда левое значение можно только принудительно (через ручное преобразование типов).
Цитата(scifi @ Jun 22 2007, 12:47)

Я где-то читал, что если не использовать виртуальные функции, то всё, что делается на Си++, можно сделать и на Си. Так что стоит ли огород городить?
И виртуальные функции - точнее, функциональность, реализуемую ими, можно достаточно просто реализовать на С. И вообще, любую программу можно написать на ассемблере.
Цитата(scifi @ Jun 22 2007, 12:47)

Моё личное мнение - никаких проблем с #define я тут не вижу.
Например, такую проблему Вы не видите?
Код
#define DOUBLE(x) x+x
int a = 3*DOUBLE(4);
Что ожидаем получить? Ожидаем получить 3*8 = 24. А на деле что получим? А получим 3*4+4 = 16. Конечно, этот известный, описанный в учебниках эффект легко обходится с помощью применения скобок:
#define DOUBLE(x) (x+x)
Но там и кроме этого приколов хватает. По той же причине - скрытая от программиста подстановка значений безо всякого контроля со сторны компилятора. Именно по этой причине препроцессор в кодогенерации маст дай.
Цитата(scifi @ Jun 22 2007, 12:47)

Как верно сказал DRUID3, enum хорош тем, что возможна проверка типов, но при этом придётся его явно преобразовывать к int там, где это нужно.
Какая именно провека типов имеется в виду?
Цитата(scifi @ Jun 22 2007, 12:47)

Конечно, пуристы из лагеря Си++ скажут, что #define - это тяжёлое наследие Си, и что в Си++ есть средства получше. Но только Вам решать, что использовать, а что нет.
Дефайны - это наследине С. Насколько тяжелое, не берусь судить. Иногда весьма тяжеловатое. Дабы избежать пробем с дефайнами и макропостановкой, надо просто всякую вещь использовать к месту - макросы в условной компиляции, тут им замены пока нет, а в качестве констант использовать константные объекты, в качестве макрофункций - встраевыемые (inline) функции, к качестве генераторов кода - шаблоны.