В чем различие с точки зрения использования между определениями /macros/structs и consts/funcs/classes? (C++)

Я знаю, что различие между определениями и константами заключается в том, что константы имеют тип, а в макросах и функциях функции вызываются и типизируются, тогда как макросы встроенные нетипизированы. Не так много различий между структурами и классами, но я не думаю, что есть, кроме общедоступных / закрытых настроек по умолчанию (и здесь я рассматриваю только C++, а не C).

В любом случае, бывают ли случаи, когда было бы более эффективно, выгодно или, по крайней мере, более чистый код использовать определения /macros/structs, чем использование версий, констант, функций и классов C++?

В частности, и поскольку это единственное без четкого различия (по крайней мере, в конечной программе) между ними, лучше ли использовать структуры, чем классы, и, если нет эффективности, какие соглашения для их использования вы делаете? использовать?

6 ответов

Решение

Как правило, в программе на C++ я мог бы использовать struct для простого объединения данных, таких как Point структура, которая содержит x и y, Я бы использовал class для объектов, с которыми связано поведение (функции-члены).

Конечно, это всего лишь соглашение, поскольку компилятор обрабатывает их почти одинаково, за исключением двух деталей, упомянутых выше (наследование по умолчанию и видимость элемента по умолчанию).

Макросы обычно смотрят свысока (вероятно, совершенно правильно), потому что они не имеют никакой безопасности типов. Тем не менее, они полезны для тех вещей, которые вы не можете сделать непосредственно в C/C++, в частности для оператора stringize (#) для создания типов, имен переменных и перечислений в строках, которые будут встроены в код:

#define STRINGIZE(ARG) #ARG

Макросы также могут быть полезны для стандартного кода, где у вас много повторяющегося кода, например, для вставки каждого элемента перечисления в таблицу. Использовать макрос может быть намного чище, чем функцию. Таким образом, MFC использует макросы для определения обработчиков сообщений.

Более важное различие между define \macros и constants\functions - это область видимости.

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

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

Беспараметрическая #define с против const s:

Пока макрос фактически заменяется константой или переменной, которая не изменяется, оба достигают одного и того же. Но есть некоторые тонкие различия, если первое утверждение не выполняется. Давайте использовать следующий класс в качестве примера:

class my_class {
    explicit my_class(int i);
    explicit my_class(const my_class& copy_from);
};

// implementation defined elsewhere, it's irrelevant

Если вы делаете следующее:

#define MY_CONST_OBJECT my_class(1)
my_class obj1(MY_CONST_OBJECT);
my_class obj2(MY_CONST_OBJECT);

тогда конструктор my_class чей параметр является int называется дважды. И следующий код недопустим:

my_class& ref1 = MY_CONST_OBJECT;
my_class& ref2 = MY_CONST_OBJECT;

Однако, если вы делаете следующее:

const my_class my_const_object(1);
my_class obj1(my_const_object);
my_class obj2(my_const_object);

тогда конструктор my_class чей параметр является int вызывается только один раз. И изначально нелегальный код теперь легален:

my_class& ref1 = my_const_object;
my_class& ref2 = my_const_object;

параметрический #define с против inline функции:

Пока параметры макроса не являются выражениями, оба достигают одного и того же. Но есть некоторые тонкие различия, если первое утверждение не выполняется. Давайте возьмем этот макрос в качестве примера:

#define MAX(A,B) = ((A) < (B)) ? (A) : (B)

Этот макрос не мог взять rand() (или любая функция, поведение которой влияет на состояние всей программы и / или зависит от него) в качестве параметра, поскольку rand() будет вызван дважды. Вместо этого безопасно использовать следующую функцию:

template < class _Type >
_Type max( _Type a, _Type b ) { return (a < b) ? a : b ; }

struct с против class эс

Там нет никакой реальной разницы между struct с и class кроме основного спецификатора доступа по умолчанию для базовых классов и членов public и последний private, Тем не менее, нецивилизованно заявлять struct s, которые имеют методы и, следовательно, поведение, и это также не имеет смысла объявлять class Если есть только необработанные данные, к которым можно получить прямой доступ.

Объявление class который предназначен для использования в качестве struct, как это:

class my_class {
public:
    // only data members
};

в плохом вкусе. struct У конструкторов может быть несколько применений, например, запрет на инициализацию с мусорными данными, но я бы порекомендовал и против них.

Я бы предпочел функции и константы определениям и макросам, потому что они семантически более явны, поэтому вы получаете более значимую и контролируемую обратную связь от IDE и компиляторов.

В частности, и поскольку это единственное без четкого различия (по крайней мере, в конечной программе) между ними, лучше ли использовать структуры, чем классы, и, если нет эффективности, какие соглашения для их использования вы делаете? использовать?

Структуры лучше при определении блоков памяти. Например, в Windows API есть вызов с именем CreateDialogIndirect, одним из его параметров является блок памяти, описанный так:

[in] Указатель на объект глобальной памяти, содержащий шаблон, который CreateDialogIndirect использует для создания диалогового окна. Шаблон диалогового окна состоит из заголовка, который описывает диалоговое окно, за которым следуют один или несколько дополнительных блоков данных, которые описывают каждый из элементов управления в диалоговом окне. Шаблон может использовать либо стандартный формат, либо расширенный формат. В стандартном шаблоне заголовок является структурой DLGTEMPLATE, за которой следуют дополнительные массивы переменной длины. Данные для каждого элемента управления состоят из структуры DLGITEMTEMPLATE, за которой следуют дополнительные массивы переменной длины.

Все это звучит очень сложно. Конечно, если вы попытаетесь выделить кучу, выделите буфер памяти и попытайтесь заполнить его в соответствии с описанием. Однако вы можете использовать структуры для упрощения работы:

 struct {
    DLGTEMPLATE t;
    short noMenu;
    short defaultClass;
    short title;
  } templ;

  templ.t.style = style;
  templ.t.dwExtendedStyle = extendedStyle;
  templ.t.cdit = 0;
  // etc...
  HWND hDlg = ::CreateDialogIndirectParam(hInstance,
                                          &templ.t,
                                          hWndParent,
                                          (DLGPROC)DialogProc,
                                          NULL);

Я думаю, что это ситуация, когда структуры являются лучшим выбором, чем классы. Это будет работать с классом, но это будет выглядеть очень странно, вы не согласны?

Другие вопросы по тегам