Информация об изменениях

Сообщение Проектирование класса от 17.10.2023 18:11

Изменено 17.10.2023 18:18 Sm0ke

Проектирование класса
Приветики
После некоторых занятий на белке и ответов в rsdn на тему других языков я решил написать немного своих размышлений про мой любимы си++

Чем отличается структура от класса — мы знаем.
Это по сути казалось бы одно и тоже, вот только в struct изначально доступ к мемберам public, а в class — private
Тему наследования пока опустим для упрощения.

Я одно время ленился выбирать что писать class или struct в том или ином конкретном случае, и тупо начал писать везде struct в своём коде.
Думал мне так проще. Даже бывало делал всё public.

Бывает хочется скрыть data member от внешнего изменения. Для readonly доступа приходится писать get метод.
Таких свойств для скрытия когда становится много — то для каждого писать свой геттер — рутина.

Как быть?

Объединяем скрытые мемберы по смыслу в общую структуру. Не делаем в ней виртуальных методов, а главное доступ весь public в этой структуре.

Теперь добавляем в исходный класс одно свойство, чтобы поместить его в private / protected секцию.
На всё это свойство достаточно одного метода геттера, выдающего данные по константной ссылке.

struct Registrator_data
{
  double
  max_speed{},
  max_weight{},
  max_damage{};
};

class Registrator
{
  protected:

  // data
  Registrator_data
  m_data;

  public:

  // actions
  Registrator_data const &
  data() const { return m_data; }
};


В процессе разработки бывает что класс разрастается и не вмещается в экран.
Такой подход разделения на части призван повысить читабельность исходников.
(а не только уменшить кол-во геттеров при скрытии)

Про friend declaration ещё хотелось бы упомянуть.
Вот не нравится мне писать friend в си++ для предоставления доступа к мемберам.
А зачем это вообще нужно? Например когда передаём this всего класса во внешнюю функцию.
Так же проще, взял да написал friend. А как иначе?

Не полениться бы и порассуждать логически.
Если внешней функции нужны скрытые параметры класса, то можно их туда передать пачкой (вместо this / отдельно от this).
Для этого вообще не нужно писать friend declaration.
Более того можно передать по константной ссылке в случае readonly доступа — через метод data(), или для полного доступа — просто m_data.

Подытожу мысль.
class — для сокрытия и для виртуальности.
struct — это просто набор свойств.

Получилось подобие мини article, а не впорос.

--

Случаи с наследованием тоже бы рассмотреть в таком ключе, по которому реплаи прошу постить отдельно от реплаев на article выше.

Как вы относитесь к наследованию у struct, или используете в нём только инкапсуляцию?
Тот же вопрос: как в class -ах наследования используете?

Наследование методов от базы не всегда есть хорошо.
Есть ли способ запретить в потомке вызов из public для отдельного метода из базы?
Например оператор копирующего присваивания в базе.
* Но чтобы некоторые остальные методы из базы остались как public
Проектирование класса
Приветики
После некоторых занятий на белке и ответов в rsdn на тему других языков я решил написать немного своих размышлений про мой любимы си++

Чем отличается структура от класса — мы знаем.
Это по сути казалось бы одно и тоже, вот только в struct изначально доступ к мемберам public, а в class — private
Тему наследования пока опустим для упрощения.

Я одно время ленился выбирать что писать class или struct в том или ином конкретном случае, и тупо начал писать везде struct в своём коде.
Думал мне так проще. Даже бывало делал всё public.

Бывает хочется скрыть data member от внешнего изменения. Для readonly доступа приходится писать get метод.
Таких свойств для скрытия когда становится много — то для каждого писать свой геттер — рутина.

Как быть?

Объединяем скрытые мемберы по смыслу в общую структуру. Не делаем в ней виртуальных методов, а главное доступ весь public в этой структуре.

Теперь добавляем в исходный класс одно свойство, чтобы поместить его в private / protected секцию.
На всё это свойство достаточно одного метода геттера, выдающего данные по константной ссылке.

struct Registrator_data
{
  double
  max_speed{},
  max_weight{},
  max_damage{};
};

class Registrator
{
  protected:

  // data
  Registrator_data
  m_data;

  public:

  // actions
  Registrator_data const &
  data() const { return m_data; }
};


В процессе разработки бывает что класс разрастается и не вмещается в экран.
Такой подход разделения на части призван повысить читабельность исходников.
(а не только уменшить кол-во геттеров при скрытии)

Про friend declaration ещё хотелось бы упомянуть.
Вот не нравится мне писать friend в си++ для предоставления доступа к мемберам.
А зачем это вообще нужно? Например когда передаём this всего класса во внешнюю функцию.
Так же проще, взял да написал friend. А как иначе?

Не полениться бы и порассуждать логически.
Если внешней функции нужны скрытые параметры класса, то можно их туда передать пачкой (вместо this / отдельно от this).
Для этого вообще не нужно писать friend declaration.
Более того можно передать по константной ссылке в случае readonly доступа — через метод data(), или для полного доступа — просто m_data.

Подытожу мысль.
class — для сокрытия и для виртуальности.
struct — это просто набор свойств.

Получилось подобие мини article, а не впорос.

--

Случаи с наследованием тоже бы рассмотреть в таком ключе, по которому реплаи прошу постить отдельно от реплаев на article выше.

Как вы относитесь к наследованию у struct, или используете в нём только инкапсуляцию?
Тот же вопрос: в class -ах наследование используете?

Наследование методов от базы не всегда есть хорошо.
Есть ли способ запретить в потомке вызов из public для отдельного метода из базы?
Например оператор копирующего присваивания в базе.
* Но чтобы некоторые остальные методы из базы остались как public