Какой метод лучше использовать для реализации get / set? - PullRequest
16 голосов
/ 26 июля 2011

Существует два метода реализации get / set.

Метод 1:

Определите get и установите отдельно.

class my_class
{
  // ...
};

class main_class
{
public:

  my_class get_data() const
  {
    return m_data;
  }

  void set_data(my_class value)
  {
    m_data = value;
  }

private:

  my_class m_data;
};

Примечание : в этом методе get достаточно быстр: http://cpp -next.com / archive / 2009/08 / want-speed-pass-by-value

И еще один метод (метод 2):

Определите два получающих тела: первое константное и другое неконстантное.

class my_class
{
  // ...
};

class main_class
{
public:

  const my_class& get_data() const
  {
    return m_data;
  }

  my_class& get_data() // Works like set.
  {
    return m_data;
  }

private:

  my_class m_data;
};

Используя эти методы:

void main()
{
  main_class cls;

  // For method 1.
  my_class data;
  data = cls.get_data();
  cls.set_data(data);

  // For method 2.
  const my_class data1;
  my_class data2;
  data1 = cls.get_data();  // const get invoked.
  cls.get_data() = data2; // Like set beacuase non const get invoked.

}

Мой вопрос, какой из этих методов для реализации get / set лучше?

Знаете ли вы лучший метод?


Редактировать : Для ответов, которые верят, что метод 1 лучше, что вы скажете в ситуации ниже:

void main()
{
  main_class cls;

  // For method 1.
  cls.get_data().do_something_else(); // Not effictive for cls, because data losts.

  // For method 2.
  cls.get_data().do_something_else(); // Effictive for cls.    
}

Ответы [ 8 ]

16 голосов
/ 26 июля 2011

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

class my_class
{
  // ...
};

class main_class
{
public:

  const my_class & get_data() const
  {
    return m_data;
  }

  void set_data(const my_class & data)
  {
    m_data = data;
  }

private:

  my_class m_data;
};
11 голосов
/ 26 июля 2011

Я знаю, что это не будет популярным ответом среди пуристов C ++, и до того, как я выучил Python и Ruby, я бы не стал обсуждать эту возможность ... но ... Поскольку предоставленные вами методы получения и установки не работают проверка диапазона или специальные расчеты, почему бы не сделать члена публичным?

 class main_class
 {
  public:
    my_class my_data;
 }

Конечно, вы потеряете const на геттере и вам не будет гарантирована защита, но вы все равно не гарантированы, потому что вы предоставляете функцию set, которая модифицирует член.

8 голосов
/ 26 июля 2011

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

4 голосов
/ 26 июля 2011

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

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

// First Variation
// ---------------
// In this one both the setter and the getter have the same name
// (which is the same as the member they control). To get a
// variable you do `int i = foo.var()` and to set it you do
// `foo.var(6)`. 

class Some
{
  public:
    int var() const {
        return var_;
    }

    void var(int v) {
        var_ = v;
    }

  private:
    int var_;
};

// Second Variation
// ----------------
// You can also make the setter return a reference to `this`.
// This allows you to chain setters, which can _sometimes_ be
// more readable but it also has a few disadvantages.

class Employee
{
  public:
    Employee& salary(double dollars) {
        salary_ = dollars;
        return *this;
    }

    Employee& name(const string& n) {
        name_ = n;
        return *this;
    }

  private:
    double salary_;
    std::string name_;
};

// You can now do this...
Employee emp;
emp.name("John Barlick").salary(500.00);

// ... But this can become quite ugly if you chain a large amount
// of setters (you'd then probably have to break the lines in
// order to keep the code readable). It also is (technically)
// less efficient. 

// In case you have lots of setters you could probably do this:
// emp.name("John Barlick")
//    .salary(500.00)
//    .some(787);
//    .another('g');  
2 голосов
/ 26 июля 2011

Обычно определяются геттеры / сеттеры:

  const my_class& get_data() const
  {
    return m_data;
  }

  void set_data(const my_class& _data)
  {
    m_data = _data;
  }
1 голос
/ 26 июля 2011

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

Метод 2 на самом деле то, что делают несколько контейнеров STL, например std::vector с operator[], где вы перегружаете одну и ту же функцию, но определяете одну для постоянных операций, а другую для непостоянных операций ... но Опять же, вы добавляете ненужные накладные расходы, когда вы можете просто публично получить доступ к элементу данных (т. е. вы не похожи на некоторые базовые указатели и другие элементы данных, управляемые памятью, от нас, такие как контейнер STL). Если функция, которой вы передаете ее, требует постоянной ссылки, она все равно не изменит члена, поэтому на самом деле нет необходимости создавать подобный интерфейс, если вы не пытаетесь создать общий интерфейс для доступа к члену через хост классов. И если вы делаете это, то вам следует изучить чисто виртуальный базовый класс, чтобы определить общий интерфейс.

1 голос
/ 26 июля 2011

Прежде всего, я думаю, что это не очень эффективно

void set_data(my_class value)
{
  m_data = value;
}

Вы, вероятно, должны пройти по ссылке

void set_data(const my_class& value)
{
  m_data = value;
}

Что касается метода, который вы должны выбрать, подумайте так: во втором методе вы возвращаете ссылку на ваш внутренний объект, и пользователь абсолютно свободен что-либо с ним делать. С первым методом вы можете контролировать, что пользователь может или не может делать.

0 голосов
/ 26 июля 2011

ИМХО второй способ выглядит очень неловко.

...