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

Какой метод лучше для реализации get/set?

Есть два метода для реализации 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):

Определите два тела получения: первый const и другой не const.

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 лучше?

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


Edit: Для ответов, которые верят, что метод 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.    
}
4b9b3361

Ответ 1

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

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;
};

Ответ 2

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

 class main_class
 {
  public:
    my_class my_data;
 }

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

Ответ 3

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

Ответ 4

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

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

// 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');  

Ответ 5

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

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

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

Ответ 6

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

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

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

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

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

Ответ 7

В то время как стандартные getters и settters, подобные методу 1, могут обеспечивать "инкапсуляцию", если эти функции не включены в заголовок, они добавляют много накладных расходов. Например, в узком цикле, даже если вы использовали ссылки, а не pass-by-value (для чего требуется дорогостоящая операция копирования памяти), постоянно приходится добавлять около восьми инструкций в x86 для каждого вызова геттера/сеттера в порядке настроить свою активационную запись в стеке, а также пролог и эпилог функции - использовать ценное время процессора и действительно повредить производительность. Поскольку вы получаете, а сеттеры не делают много, вам действительно не нужны.

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

Ответ 8

IMHO второй метод выглядит очень неудобно.