Вы должны прочитать (если у вас есть доступ) Эффективный Java 2 Пункт 1: Рассмотрите статические заводские методы вместо конструкторов.
Преимущества статических заводских методов:
Недостатки статических заводских методов:
Класс по сравнению со структурой
Используя класс или ключевое слово структуры является вопросом вкуса вместе с' чувство ', это производит на читателе. Технически они эквивалентны, но удобочитаемость лучше, если структуры используются для ПЕРЕХОДНЫХ ПРИСТАВОК и типов C-структуры и классов для чего-либо еще.
Основные вещи, которые должны войти в структуру C++: конструктор, который инициализирует данные (мне не нравится использовать memset, и он может позже воздержаться, если POD развивается во что-то другое), или конструкция от других типов, но не конструктора копии.
, Если необходимо определить конструктора копии или оператор присваивания, потому что сгенерированный компилятор не достаточно хорош, сделайте его классом.
распространено использовать структуры также для функторов, которые будут переданы алгоритмам STL и шаблонному метапрограммированию, как в
struct square_int {
int operator()( int value )
{
return value*value;
}
};
std::transform( v.begin(), v.end(), v.begin(), square_int() );
или
// off the top of my head
template <typename T>
struct is_pointer { enum { value = false } };
template <typename T>
struct is_pointer<T*> { enum { value = true } };
членские методы по сравнению с бесплатными функциями
Помимо того, что я сказал прежде, которые не добавляют к тому, на что уже ответили другие, я хотел поместить некоторое внимание на другие типы функций, которые Вы комментируете в своем сообщении как операторы сравнения и т.п..
Операторы, которые предназначены, чтобы быть симметричными (сравнение, арифметика), вставка и операторы удаления и преобразования, обычно лучше реализуются как бесплатные функции независимо от того, объявляете ли Вы это как класс или структуру.
операторы Symmetric (относительно типов данных) не симметричны, если они реализованы как функции членства. Правила поиска не бросят левую сторону для вызова функции членства, но она применит тот же бросок для соответствия бесплатной функции.
// Example of symmetry with free functions where method would be asymmetric
int main()
{
std::string( "Hello " ) + "world"; // compiles as free / member function
"Hello " + std::string( "world" ); // compiles with free function, fails with member function definition of +
}
В коде выше, если оператор + был членским методом станд.:: строка, которую компилятору не удалось бы скомпилировать, поскольку это не может бросить символ константы* литерал в станд.:: представьте в виде строки для использования членского метода.
Вставка и извлечение от потоков должны всегда реализовываться как бесплатные функции, поскольку поток всегда является левой стороной операции.
преобразования Хранения, поскольку бесплатные функции разъединяют эти два различных типов. Если A и' может быть преобразован друг в друга, и Вы решаете реализовать преобразования как члены A, то Необходимость знает', и все использование A будет зависеть от', используете ли Вы его или нет. Если Вы определяете преобразование как бесплатную функцию, A завершен без', и связь между этими двумя классами/структурами будет меньшей. То же идет, чтобы преобразования объединились в сеть, сериализация и десериализация. При реализации их в классе/структуре, Вы вынуждаете всех пользователей знать о тех преобразованиях.
Это - просто вопрос стиля, и что является правильным, или неправильный будет продиктован Вашим магазином. Иногда решение не будет состоять в том, чтобы принять решение, но, для перефразирования Rush, они все еще сделают выбор.
Как показывает опыт, я буду обычно использовать структуры для более простых типов данных, располагающихся от ПЕРЕХОДНЫХ ПРИСТАВОК до объектов, которые имеют участника простые функции. Но нет никакой яркой строки, и как только я определяю структуру, я не буду обычно возвращаться и изменять ее на класс просто, потому что я добавил больше функциональности. Я не вижу значения в этом.
Я не делал различие между участником и бесплатными функциями, когда я думал о вопросе, но я теперь вижу, что это было ошибкой. Мне теперь кажется, что структуры должны только редко иметь функции членства. Довольно ясно, что все в структуре должно быть общедоступным.
Поэтому обычно нет никакого смысла в наличии функций членства структуры, потому что любая функция может изменить данные в структуре. Функция членства на структуре была бы удобством, а не необходимостью.
Единственными исключениями были бы вещи, требуемые быть функциями членства для некоторой другой цели - конструкторы для инициализации массивов; сравнения для использования с картой; вещи используются шаблонами; и т.д.
Возможно, класс и структура должны рассматриваться как противоположности - класс выставляет функции и скрывает данные, тогда как структура выставляет данные и позволяет Вам скрывать функции.
Возвращение к примеру свопинга байта:
struct S
{
int data;
S() {data = 1234;}
void ByteSwap() {network::ByteSwap( &data );}
};
S s;
s.ByteSwap();
стал бы:
struct S
{
S() {data = 1234;}
int data;
};
namespace network
{
void ByteSwap( int* i ) {/*byte swap *i*/}
void ByteSwap( S* s ) {ByteSwap( &s->data );}
S s;
ByteSwap(&s);
}
Это имеет смысл, когда данные и некоторые функции не всегда сильно принадлежат вместе. Свопинг байта только представлял бы интерес для сетевой системы, но высокоуровневые функции могут все еще использовать структуру, даже не зная о низкоуровневом материале как свопинг байта.
Другое преимущество в этом случае - то, что связанные операции свопинга байта все держатся вместе в том же месте.
Мое простое эмпирическое правило для структур и классов:
if (data_requires_strict_alignment == TRUE) {
use(struct);
} else {
use(class);
}
таким образом, если данные Вы представляете, соответствует некоторому объекту данных, который сделал, чтобы строгий участник заказал и требования выравнивания (например, структура данных, которой обмениваются с аппаратными средствами на уровне драйвера), использовал структуру. Для всех других случаев используйте класс. Классы имеют столько функций и возможностей, которые не делают структуры, и в моих событиях выгодно использовать классы, когда это возможно, даже если Вы не используете ни одной из тех дополнительных функций в данный момент (если ничто иное, класс только для данных похож на структуру с более безопасным уровнем доступа по умолчанию). Зарезервируйте структуры для тех случаев при необходимости в уникальных свойствах структуры; а именно, способность определить элементы структуры в точном порядке и с точным выравниванием/дополнением (обычно с коммуникацией низкого уровня, например, драйверами) таким образом, что можно бросить его к массиву байтов, использование memcpy()
на ней, и т.д. Если бы Вы следуете этой модели, тогда класс, не использовалась бы в качестве члена структуры (так как определение класса не определяет выравнивание или предсказуемое значение для sizeof(class)
). Аналогично, если Вы думаете о конструкторах или операторах, используйте класс.
Это эмпирическое правило также помогает помочь взаимодействовать через интерфейс с кодом C, так как структуры используются способом, согласовывающимся со структурами C.
Обычно я использую class
каждый раз, когда я должен использовать спецификаторы доступа. Это имеет эффект, которым большая часть моего материала верхнего уровня остается как классы, в то время как редкие наборы POD и мой not-so-rare внутренний класс pimpls обычно являются структурами.
Я использую структуру каждый раз, когда я хочу использовать элемент данных в качестве интерфейса объекта. Я изменил бы структуру на класс, каждый раз, когда существует потребность добавить частный раздел.
Я всегда использую структуры для 'глыб данных', даже если они украшены конструктором и иногда операторами сравнения, они никогда не добавляли методы к ним (даже не добираются/методы установки).
я думаю, что это показывает намерение типа - структура является просто данными, на которых будут управлять другие вещи.
Единственные методы мне нравится ставить структуры, являются методами типа свойства, простые преобразования (и, при необходимости для типа, операторов), и не по умолчанию конструкторы.
, Например, я мог бы определить RECT
структура как указано ниже:
typedef struct tagRECT{
int left;
int top;
int right;
int bottom;
int get_width(){return right - left;}
int get_height(){return bottom - top;}
int set_width(int width){right = left + width; return width;}
int set_height(int height){bottom = top + height; return height;}
} RECT, *PRECT;
методы "набора" возвращают новое значение для поддержки объединения в цепочку присвоения в случае, что компилятор поддерживает свойства как расширение.
Для типов POD, которые хранят сложные данные, я мог бы включать методы, которые выполняют простые преобразования на тех данных. Очевидный пример мог бы включать, Вращаются, Масштаб, Сдвиг, и Переводят методы на структуре TransformationMatrix.
Действительно просто расширение вышеупомянутого, если операторы имеют смысл для типа, я добавлю их к структуре. Это может быть соответствующим и необходимым для поддержания атомарности объекта. Очевидным примером являются стандартные арифметические операторы на Сложной структуре.
я предпочитаю не иметь конструктора по умолчанию на своих структурах. Я не хочу выделять массив ПЕРЕХОДНЫХ ПРИСТАВОК и переносить вызов тысячи (или безотносительно) инициализации по умолчанию. Если memset
инициализация не будет достаточна, я предоставлю Инициализировать метод.
я, однако, предоставлю конструктору не по умолчанию на структурах. Это особенно полезно, когда одно или несколько полей могут быть выведены из частичной конструкции.
Если существуют accesser методы для элементов данных тогда, это - класс.
, Если Вы имеете прямой доступ к данным и можете изменить его по желанию, это - структура.
нет никакой причины, структура не должна иметь конструкторов, операторов сравнения, и т.д.
Другое общее использование структур для шаблонного материала и локального RAII или классов функтора. Это одноразовые биты кода, которые ближе к функциям, чем они к целым классам и требованию дополнительного public:
, объявление просто глупо. При рассмотрении повышения Вы будете видеть много материала как это:
template<
bool C
, typename T1
, typename T2
>
struct if_c
{
typedef T1 type;
};
Это - ясно не POD (на самом деле, это не содержит данных вообще). В других случаях у Вас может быть класс RAII, которые состоят только из constructor/desctructor:
struct LogOnLeavingScope : public NonCopyable
{
string Message;
LogOnLeavingScope(const string &message) : Message(message) {}
~LogOnLeavingScope() { Log(Message); }
];
классы Функтора предложили бы тот же аргумент:
struct Logger
{
void operator()(const string &message) { Log(message); }
}
я сказал бы, что нет никакой функции C++, который подразумевает, что необходимо использовать класс вместо структуры (кроме, возможно, виртуальных функций). Это - все о том, какой интерфейс Вы хотите представить - используют структуру, если Вы соглашаетесь со всем быть общедоступным. Если Вы находите, что желаете добавить private:
разделы, что хороший знак Вы действительно хотите класс вместо структуры.
Непротиворечивость является самой важной. Точка конвенций должна дать общий ориентир для всех те, которые читают Ваш код в будущем.
Лично, я избегаю структур, если я чувствую, что мне нужна функциональность класса.
иначе: "простые данные" подход.
Взгляд на Ваш проект и установленный норма, или уже придерживаются той там.
, Хотя, я предлагаю, Вы стараетесь избегать наследования, особенно если вся структура делает, содержит POD. Ничто худшее тогда трассировка набора суперклассов для целого числа или символа.
Я добавлю очень немного методов к структуре, для удобства, только если я на самом деле использую их. Конечно, все они общедоступны. Если мне больше нужно, чем это, это сразу преобразовывается в класс.
Я мог бы быть в меньшинстве, но я использую struct
с для значения одной вещи, "порядок битов имеет значение". Что-либо, что должно быть сериализировано к диску или сети, или должно быть совместимо с некоторой сторонней библиотекой и должно быть в правильном порядке, Или если это делает некоторое волшебство битового поля как процессор определенная оптимизация, которая всегда входит struct
. Реконструкция полей в struct
, поэтому, всегда имеет некоторые последствия и должна быть тщательно продумана.
class
es, однако, имеют поля, которые только семантически значимы. Если по некоторым причинам я или кто-то еще хотим перестроить или изменить элементы данных class
, это может произойти довольно свободно.
Вы могли посмотреть на то, что делает Стандартная Библиотека. Общая любимая структура станд.:: пара только имеет конструкторов.
я нахожу использование конструкторов со структурами столь удобным и естественным, что я не могу предположить обходиться без них. Я никогда не даю структурам никакие другие методы, но конечно могут быть бесплатные функции или члены других классов, которые берут их в качестве параметров.
Я использую классы и инкапсуляцию, когда я должен поддержать инварианты и целостность данных. Если у Вас нет инвариантов, и данные действительно являются просто блоком объектов, всегда было хорошо в нашем магазине использовать структуру, даже если Вы добавляете необычных конструкторов помощника или функции. Однако больше Вы действительно украшаете его, который должен заставить Вас остановиться и думать, что, возможно, это должен быть класс.
, Если действительно необходимо быть совместимым POD (для говорят, что взаимодействие через интерфейс к коду C) действительно все еще необходимо использовать структуру. Однако можно обернуть эту структуру в класс и представить ее с получением () функция для взаимодействия через интерфейс с API C. Или создайте функцию помощника для возврата надлежащей структуры POD из класса.
От прочтения некоторого исходного кода STL, который поставлется с Visual Studio, кажется, что используемые критерии являются действительно ли вещами, "главным образом общедоступны" (запустите с структура ), или "главным образом частный" (запускаются с класс )?
В том же духе, если, что Вы пишете около вершины (возможно, потому что это важно) Вашего класса общественность , затем пойдите с структура . С другой стороны, если Вы перечисляете членские данные, сначала используют класс .
Мое персональное предпочтение состоит в том, чтобы только использовать структуры, при отсутствии методов вообще. Если я должен добавить метод по какой-либо причине, это - класс.
Я думаю, что существует три главных, когерентных философских школы:
struct
и class
попеременно. struct
с только для представления маленького POD. struct
с в качестве записей. я не могу привести окончательный аргумент в пользу ни одной из этих стратегий. Я склонен следовать за путем 2, но я также использую структуры для типов не-POD, когда я вижу, что он соответствует, специально для функциональных объектов (даже если они не могут выполнить требования POD).
(Кстати, облегченный FAQ C++ имеет довольно хорошее определение POD).
РЕДАКТИРОВАНИЕ я не коснулся шаблонных методов метапрограммирования, таких как использование struct
для заполнителей (введите теги) или реализовать метафункции. Я предполагаю, что в этих случаях нет абсолютно никакого противоречия: так как они никогда не содержат методы (или даже данные), всегда используйте struct
).
многие люди, включая меня, используют структурировать или класс продемонстрировать намерение - структуры для группировки "простых старых данных" и классы для инкапсулированных данных, которые проводит значимые операции.
ИМХО, эта дифференциация - недоразумение, но понятно, почему она так используется. Оно основано на традиционных конвенциях и встроенном ощущении, что класс против структуры. Я бы последовал предложению Маршалла Клайна:
7.8 В чем разница между ключевыми словами struct и классом?
Так как это коннотация больше всего у людей уже есть, ты должен вероятно, использовать ключевое слово struct, если вы иметь класс, который имеет очень мало методов и имеет открытые данные (такие вещи делают существуют в хорошо спроектированных системах!), но в противном случае вам, вероятно, следует использовать ключевое слово класса.
Лично я (более)использую struct
ключевое слово для метафункций