Соглашения для методов доступа (геттеры и сеттеры) в C ++ - PullRequest
71 голосов
/ 05 сентября 2010

В SO было задано несколько вопросов о методах доступа в C ++, но ни один из них не смог удовлетворить мое любопытство по этому вопросу.

Я стараюсь по возможности избегать методов доступа, потому что, как и Страуструп, и другие известные программисты, я считаю класс, многие из которых являются признаком плохого ОО. В C ++ я могу в большинстве случаев добавить больше ответственности к классу или использовать ключевое слово friend, чтобы избежать их. Однако в некоторых случаях вам действительно нужен доступ к конкретным ученикам.

Есть несколько возможностей:

1. Не используйте аксессоры вообще

Мы можем просто сделать соответствующие переменные-члены открытыми. Это не разрешено в Java, но, похоже, все в порядке с сообществом C ++. Тем не менее, я немного беспокоюсь о случаях, когда явная копия или ссылка на объект (только для чтения) (const) должны быть возвращены, это преувеличено?

2. Используйте методы get / set в стиле Java

Я вообще не уверен, что это с Java, но я имею в виду следующее:

int getAmount(); // Returns the amount
void setAmount(int amount); // Sets the amount

3. Используйте объективные методы получения / установки в стиле C

Это немного странно, но, видимо, все чаще встречается:

int amount(); // Returns the amount
void amount(int amount); // Sets the amount

Чтобы это работало, вам нужно будет найти другое имя для вашей переменной-члена. Некоторые люди добавляют подчеркивание, другие - "m_". Мне тоже не нравится.

Какой стиль вы используете и почему?

Ответы [ 9 ]

39 голосов
/ 06 сентября 2010

С моей точки зрения, когда я работаю с 4 миллионами строк кода C ++ (и это всего лишь один проект) с точки зрения обслуживания, я бы сказал:

  • Это нормально, не использовать геттеры / сеттерыесли члены являются неизменяемыми (т. е. const) или простыми без каких-либо зависимостей (например, точечный класс с элементами X и Y).

  • Если элемент только private, то также можнопропустить геттеры / сеттеры.Я также считаю членов внутренних pimpl -классов как private, если единица .cpp является маленькой.

  • Если член public или protected (protected такой же плохой, как public) и не- const, непросто или имеет зависимости, тогда используйте геттеры / сеттеры.

Asпарень по обслуживанию моя главная причина желания иметь геттеры / сеттеры состоит в том, что тогда у меня есть место для установки точек останова / ведения журнала / чего-то еще.

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

7 голосов
/ 05 сентября 2010

2) - лучшее ИМО, потому что оно проясняет ваши намерения. set_amount(10) более значим, чем amount(10), и в качестве приятного побочного эффекта позволяет член с именем amount.

Публичные переменные обычно плохая идея, потому что нет инкапсуляции. Предположим, вам нужно обновить кэш или обновить окно при обновлении переменной? Жаль, если ваши переменные общедоступны. Если у вас есть метод set, вы можете добавить его туда.

7 голосов
/ 05 сентября 2010
  1. Я никогда не использую этот стиль.Потому что это может ограничить будущее вашего класса, и явные геттеры или сеттеры столь же эффективны с хорошими компиляторами.

    Конечно, в действительности встроенные явные геттеры или сеттеры создают такую ​​же основную зависимость от реализации класса,Просто уменьшите семантическую зависимость.Вам все равно придется перекомпилировать все, если вы их измените.

  2. Это мой стиль по умолчанию, когда я использую методы доступа.

  3. Этот стиль кажется слишком«Умный» для меня.Я использую его в редких случаях, но только в тех случаях, когда я действительно хочу, чтобы аксессор чувствовал себя как можно больше как переменная.

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

6 голосов
/ 06 сентября 2010
  1. Это хороший стиль, если мы просто хотим представить pure данные.

  2. Мне это не нравится :), потому что get_/set_ действительно не нужно, когда мы можем перегрузить их в C ++.

  3. STL использует этот стиль, например std::streamString::str и std::ios_base::flags, за исключением случаев, когда его следует избегать! когда? Когда имя метода конфликтует с именем другого типа, тогда используется стиль get_/set_, например std::string::get_allocator из-за std::allocator.

4 голосов
/ 06 сентября 2010

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

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

a.Это дает классу возможность решить, кому разрешить доступ к его частному состоянию

b.Это дает классу возможность решить, какой доступ разрешить каждому из лиц, которые заинтересованы в его частном состоянии

c.Он четко документирует такой внешний доступ через понятный интерфейс класса

Основная идея такова:

a) Редизайн, если это возможно,

b) Рефакторинг такой

  1. Весь доступ к состоянию класса осуществляется через хорошо известный индивидуалистический интерфейс

  2. Должно быть возможно настроить некоторыевид того, что можно и чего нельзя делать для каждого такого интерфейса, например, должен быть разрешен весь доступ от внешнего объекта ХОРОШО , весь доступ от внешнего объекта ПЛОХО должен быть запрещен, а внешний объект OK должно быть разрешено получать, но не устанавливать (например)

2 голосов
/ 05 сентября 2010
  1. Я бы не исключал использование аксессоров. Может быть для некоторых структур POD, но я считаю их хорошей вещью (некоторые аксессоры могут иметь дополнительную логику)

  2. Не имеет значения соглашение об именах, если вы последовательны в своем коде. Если вы используете несколько сторонних библиотек, они могут использовать разные соглашения об именах. Так что это дело вкуса.

1 голос
/ 21 июля 2017

Я видел идеализацию классов вместо целочисленных типов для ссылки на значимые данные.

Что-то подобное ниже, как правило, плохо использует свойства C ++:

struct particle {
    float mass;
    float acceleration;
    float velocity;
} p;

Почему?Потому что результатом p.mass * p.acceleration является float, а не сила, как ожидается.

Определение классов для обозначения цели (даже если это значение, например, сумма , упомянутое ранее) имеет больше смысла и позволяет нам делать что-то вроде:

struct amount
{
    int value;

    amount() : value( 0 ) {}
    amount( int value0 ) : value( value0 ) {}
    operator int()& { return value; }
    operator int()const& { return value; }
    amount& operator = ( int const newvalue )
    {
        value = newvalue;
        return *this;
    }
};

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

struct wage
{
    amount balance;

    operator amount()& { return balance; }
    operator amount()const& { return balance; }
    wage& operator = ( amount const&  newbalance )
    {
        balance = newbalance;
        return *this;
    }
};

Использование Getter / Setter:

void wage_test()
{
    wage worker;
    (amount&)worker = 100; // if you like this, can remove = operator
    worker = amount(105);  // an alternative if the first one is too weird
    int value = (amount)worker; // getting amount is more clear
}

Это другой подход, не означает, что он хороший или плохой, но другой.

0 голосов
/ 15 февраля 2014

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

Необходимо прочитать и изменить

Просто объявите эту переменную public:

class Worker {
public:
    int wage = 5000;
}

worker.wage = 8000;
cout << worker.wage << endl;

Нужно просто прочитать

class Worker {
    int _wage = 5000;
public:
    inline int wage() {
        return _wage;
    }
}

worker.wage = 8000; // error !!
cout << worker.wage() << endl;

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

0 голосов
/ 06 сентября 2010

Дополнительная возможность может быть:

int& amount();

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

str.length() = 5; // Ok string is a very bad example :)

Иногда это может быть просто хороший выбор:

image(point) = 255;  

Еще одна возможность, снова использовать функциональную нотацию для изменения объекта.

edit::change_amount(obj, val)

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

...