класс — Пассивные объекты в переполнении стека

Стиль Google C ++ руководство советует использовать структуры для пассивных объектов в отличие от классов. Каковы хорошие примеры использования (с примерами) пассивных объектов?
Каковы преимущества использования структур (по сравнению с классами) для пассивных объектов?

0

Решение

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

struct Point
{
float x, float y;
}

Точка не будет технически взаимодействовать с другими объектами, это пассивный.

без пассивного Объект будет объектом, который будет использовать другие объекты для выполнения задач, особенно с функциями. Написание класса, который бы обрабатывал сетевой ввод / вывод, должно быть классом, так как он будет использовать множество других объектов для обработки сокетов, адресов и т. Д. Быстрый пример из текущей программы:

Что касается преимуществ, наличие структуры означает, что вам не нужно иметь gettors / settors для ваших членов. Структурные члены / методы неявно public если явно не определено так. Это означает меньше кода, а также небольшое улучшение памяти. Если у объекта нет функций, таблица функций не будет создана. Не такой большой, но все же улучшение. У вас также нет накладных расходов на вызовы функций.

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

enum IRC_RESPONSE
{
RSP_IGNORE = 0,
RSP_CONTINUE,
RSP_RET_TRUE,
RSP_RET_FALSE,
RSP_BREAK
};

class IRC_Client
{
public:
//Initialize all sockets and stuff given IP, port, etc
bool Initialize(const char *address, const int port);

//login to server, give login info
bool Login(const char *password, const char *nickname, const char *user);

//disconnect
bool Disconnect();

//main update loop
void Run();

//join/leave channel
void Join(const std::string channel);
void Part(const std::string channel);

//main update

private:
// MEMBERS ////////////////////////////////////////////////////////
SOCKET ClientSocket_;         //socket of the client
sockaddr_in *ServerAddress_;  //address of the server
std::string Nickname_;        //nickname of user

// METHODS ////////////////////////////////////////////////////////
//parsing user input to decide what to do
bool HandleUserInput(std::string message);

//handling any message from server
IRC_RESPONSE HandleServerMessage(std::string message);

//send message
void SendMessage(std::string message);
void SendMessage(std::string prefix, std::string command, std::string         parameters, std::string trail);

// UTILITIES //////////////////////////////////////////////////////
//pull nickname from prefix
std::string GetNickname(std::string prefix);

//parse
void ParseServerMessage(std::string message, std::string &prefix,     std::string &command, std::string &end, std::vector<std::string> &parameters);

// USER INPUT /////////////////////////////////////////////////////
static void *_inputThread(void*);
};

Этот класс совсем не пассивен! Я использую такие структуры, как sockaddr_in, Я также использую некоторые классы, такие как std::string, Делать это классно — это хорошо, потому что я скрываю реализацию для пользователя, чтобы жизнь была простой.

2

Другие решения

Каковы хорошие примеры использования (с примерами) пассивных объектов?

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

Иногда вам не нужна такая инкапсуляция, и вам необходимо подчеркнуть разработчикам, что эта свобода не существует и вместо этого «клиенты» на каком-то уровне — в другом месте того же класса (для вложенных классов / структур) или пространства имен, файла реализации , библиотека, приложение или «система», связанная с IPC, сознательно позволил связать себя с выбранными членами данных.

Это может быть сделано, потому что:

  • Вы хотите конкретное представление данных, которое соответствует необходимой кодировке при чтении или записи памяти или потоков, потому что есть соглашение с клиентами (-ями) об этих элементах данных или макете (например, протокол ping, стандартный протокол Arrowhead Токийской фондовой биржи, MS Excel версии 7.0 стандарт документа, подпрограмма на ассемблере, которую вы будете вызывать, которая ожидает указатель на конкретные данные, некоторая аппаратная шина, доступ к которой осуществляется через «память» чтения / записи), которая «больше» (более стабильная / более длительная, официальная), чем любая отдельная обработка данных реализация: возможность изменения данных настолько крайне маловероятна (или невозможна — вы не можете ретроспективно изменить формат данных, используемый версией Excel с прошлого года) и / или фундаментально, что вы будете готовы к тому, что ваша программа прервется и получит пересмотреть все виды использования данных, чтобы исправить это.

  • Область применения «клиента» относительно невелика (например, закрытый класс / структура, вложенные в другой или один в анонимном пространстве имен в файле реализации), так что в случае изменения данных требуется ограниченная доработка, и есть сеть Написание кода / поддержка / понятность выигрывают от простоты и краткости открытых элементов данных, а также от отказа от спецификаторов доступа и тривиальных функций get / set.

Пример: Weighted_Average класс, который внутренне хранит список из многих образцов с временными метками, когда они были захвачены — он может разумно смешать экземпляр типа данных вместе с, скажем, timeval или же std::chrono::time_point в struct он может использовать для создания экземпляра vector или же list,

Каковы преимущества использования структур (по сравнению с классами) для пассивных объектов?

Есть минимальные функциональные преимущества (единственное языковое различие заключается в доступе по умолчанию к базам и членам, что делает struct более лаконично, если вы хотите такой разоблачить).

Мотивация для руководства по стилю Google ясно выражена в этом:

Мы добавляем наши собственные смысловые значения к каждому ключевому слову

Это просто говорит, что они используют struct против class кодировать, является ли тип пассивным (по их определению) или нет.


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

  • получение конкретного наблюдателя от абстрактного наблюдателя при переопределении виртуальных обратных вызовов для различных событий, где наблюдатель был закрыт для класса, в котором он в любом случае использовался

  • написание простых функторов без состояния (например, Hash, Equal для создания экземпляров std::unordered_map) или предикаты

  • частный struct Impl; за Идиома pImpl

Кроме того, я решаю, основываясь на зависимостях / связях, которые я готов предоставить клиентам, что не связано с тем, struct будет иметь более сложные функции-члены. Я не вижу веских причин для ограничения Google на struct функции-члены, в которых эти функции не делают претензий на принудительное применение постусловий или инвариантов, которые на самом деле неосуществимы с учетом прямого доступа, доступного для членов данных. Другими словами — до тех пор, пока structДанные хороши после вызова функции, кого волнует, сделала ли функция. В качестве конкретного примера, если struct timeval { int64_t tv_sec, tv_usec; }; были даны normalise() функция, которая обеспечила tv_usec находился в микросекундном диапазоне [0, 1 000 000) и иногда корректировался tv_sec соответственно, хорошо и хорошо: это не мешает любому другому использованию данных.

1