Подтвердить что ты не робот

С++ Структуры с функциями-членами против классов с общедоступными переменными

Это действительно вопрос хорошей формы/лучших практик. Я использую structs в С++ для создания объектов, которые предназначены для хранения данных в основном, а не для создания класса с тонны методов доступа, которые ничего не делают, кроме получения/установки значений. Например:

struct Person {
    std::string name;
    DateObject dob;
    (...)
};

Если вы представляете себе еще 20 переменных, писать это как класс с частными членами и 40-х элементов-аксессуаров - это боль для управления и кажется мне расточительной.

Иногда, хотя, возможно, мне также потребуется добавить некоторую минимальную функциональность для данных. В примере, например, мне также иногда нужен возраст, основанный на dob:

struct Person {
    std::string name;
    DateObject dob;
    (...)
    int age() {return calculated age from dob;}
}

Конечно, для любой сложной функциональности я бы сделал класс, но для просто простой функции, такой как этот, "плохой дизайн"? Если я использую класс, это плохая форма, чтобы сохранить переменные данных в качестве публичных членов класса, или мне просто нужно принять их и сделать классы с кучей методов доступа? Я понимаю различия между классами и структурами, я просто спрашиваю о лучших практиках.

4b9b3361

Ответ 1

Я думаю, что здесь есть два важных принципа проектирования:

  • Скрыть представление класса через интерфейс, если в этом классе есть некоторый инвариант.

    Класс имеет инвариант, если для этого класса существует такая вещь, как недопустимое состояние. Класс должен постоянно сохранять свой инвариант.

    Рассмотрим тип Point, который представляет собой двумерную геометрическую точку. Это должно быть просто struct с общедоступными членами x и y. Нет такой вещи, как недопустимая точка. Каждая комбинация значений x и y отлично подходит.

    В случае a Person, независимо от того, имеет ли он инварианты, полностью зависит от рассматриваемой задачи. Вы считаете такие вещи, как пустое имя, как действительное имя? Может ли Person иметь любую дату рождения? Для вашего случая, я думаю, что ответ "да", и ваш класс должен публиковать своих членов.

    Смотрите: Классы должны использовать инварианты

  • Нечеловеческие функции, не являющиеся членами, улучшают инкапсуляцию.

    Нет причин, по которым ваша функция age должна быть реализована как функция-член. Результат age может быть рассчитан с использованием общедоступного интерфейса Person, поэтому у него нет причин быть функцией-членом. Поместите его в том же пространстве имен, что и Person, чтобы он был найден зависимым от аргумента поиска. Функции, найденные ADL, являются частью интерфейса этого класса; они просто не имеют доступа к конфиденциальным данным.

    Если вы сделали его функцией-членом и однажды представили какое-то частное состояние в Person, у вас возникнет ненужная зависимость. Внезапно age имеет больший доступ к данным, чем нужно.

    Смотрите: Как функции, отличные от членов, улучшают инкапсуляцию

Итак, как бы я его реализовал:

struct Person {
  std::string name;
  DateObject dob;
};

int age(const Person& person) {
  return calculated age from person.dob;
}

Ответ 2

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

Ответ 3

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

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

Скажем, у вас есть класс A, у которого есть функция GetSomeVariable, которая является просто получателем для частной переменной:

class A
{
    double _someVariable;

public:
    double GetSomeVariable() { return _someVariable; }
};

Что, если через двадцать лет после линии значение этой переменной изменится, и вы должны, допустим, умножить ее на 0,5? При использовании геттера это просто; просто верните переменную, умноженную на 0,5:

    double GetSomeVariable() { return 0.5*_someVariable; }

Благодаря этому вы обеспечиваете удобство обслуживания и облегчаете его.

Ответ 4

Если вам нужен какой-то держатель данных, то предпочитайте struct без каких-либо методов get/set.

Если этого больше, как в этом случае "Человек".

  • Он моделирует объект реального мира,
  • Имеет определенное состояние и поведение,
  • Взаимодействует с внешним миром,
  • Экспортирует простую/сложную связь с другими объектами,
  • он может развиваться сверхурочно,

то это идеальный кандидат для класса.

Ответ 5

"Использовать структуру только для пассивных объектов, которые несут данные, а все остальное - это класс".

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

Ответ 6

Я не хочу сверкать святой войной здесь; Я обычно различаю его следующим образом:

  • Для объектов POD (т.е. только для данных, без видимого поведения) объявлять внутреннюю публикацию и обращаться к ним напрямую. Использование ключевого слова struct удобно здесь, а также служит подсказкой использования объекта.
  • Для объектов, отличных от POD, объявляет внутреннюю информацию частным и определяет публичные геттеры/сеттеры. Использование ключевого слова class более естественно в этих случаях.