2016-07-13 5 views
5

Я разработчик Java, пытающийся забрать C++. Можно ли использовать сеттер внутри конструктора для повторного использования проверок здравомыслия, которые предоставляет сеттер?Использование seters в конструкторе

Например:

#include <stdexcept> 
using namespace std; 

class Test { 
    private: 
     int foo; 
     void setFoo(int foo) { 
      if (foo < 42) { 
       throw invalid_argument{"Foo < 42."}; 
      } 

      this->foo = foo; 
     } 

    public: 
     Test(int foo) { 
      setFoo(foo); 
     }; 
}; 
+0

Действительно. Обратите внимание, что существуют типы 'unsigned', чтобы избавиться от теста. – Jarod42

+0

Если вы не хотите, чтобы пользователи класса не проходили отрицательные числа, почему бы не использовать 'unsigned' вместо этого? Затем компилятор будет обрабатывать проверку во время компиляции для вас, а не вам нужна проверка времени выполнения. –

+2

«неподписанный» материал не бесспорен; см., например, https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 9:50, 42:40, 1:02:50 (Группа, в которой утверждают несколько видных членов комитета против использования целых чисел без знака для других вещей, кроме бит-возиться.) –

ответ

5

Да, рекомендуется делать это, в основном по той причине, о которой вы уже упоминали.

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

Еще одна рекомендация: Если переменная класса является объектом, и вы можете изменить конструктор этого объекта, вы можете поставить галочку в конструктор этого объекта:

class MyFoo { 
public: 
    MyFoo(int value) { 
     if (value < 42) { 
      throw invalid_argument{"Foo < 42."}; 
     } 
     v = value; 
    } 
private: 
    int v; 
} 

Это позволит Вам использовать список инициализации в конструкторе класса Test:

Test(int foo) : foo(foo) {} 

Однако теперь чек является собственностью класса переменной и больше не один из имущего класса.

1

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

3

Да, вы можете. Это нормально, пока ваши сеттеры не virtual, потому что это иерархия наследования при вызове правильных функций, поскольку «этот» ptr еще не готов.

Вот Herb Sutter GOTW по этому вопросу: http://www.gotw.ca/gotw/066.htm

+0

@ user2079303 Я согласен, что все в порядке, пока разработчик знает, какая функция будет вызвана в такой ситуации. Я никогда не думал об ошибках, возникающих при вызове не виртуальных функций, вызывающих виртуальные onces. Хорошая точка зрения. – paweldac

+0

@ user2079303: _ «То, что вы абсолютно не делаете, это вызвать функцию-член, которая в свою очередь вызывает виртуального участника» _ Почему нет? Как это отличается от того, что вы сказали просто безопасно? –

+0

@ user2079303: «Боюсь, что так. Неконструкторы выполняют динамическую отправку, а конструкторы выполняют динамическую отправку. Результат детерминирован. Возможно, это не так, как вы ожидали, если вы в середине создания объекта с более производными, но [он «безопасен» и, безусловно, имеет тот же эффект в обоих случаях] (http://coliru.stacked-crooked.com/а/9a0f6498a5df9acb). –

1

Короткий ответ: Да. Фактически, ваш пример работает.

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

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

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

Например, если в вашем классе вы измените setFoo в будущем (допустим, setFoo изменяет член foo, только он больше), вы, например, перестаете работать.

0

Я знаю, что это не соответствует вашей ситуации. Его только для полноты:

Когда вы просто устанавливаете значения элементов настроек (без проверок, подобных вашим в setFoo), рекомендуется использовать списки инициализации в конструкторе. Это предотвращает «инициализацию» членов 2 раза: 1. с их значением по умолчанию, 2. со значением, которое вы передали в конструктор.

class Test { 
private: 
    int foo_; 

public: 
    Test(int foo) 
     : foo_(foo) 
    { }; 
}; 
Смежные вопросы