Путь к защитной проверке значения, присвоенного publi c константной переменной в неизменяемом классе в C ++ 17? - PullRequest
0 голосов
/ 06 февраля 2020

Возвращение к C ++ после перерыва в Java. Попытка создать неизменный объект и после работы в Java, переменная publi c const кажется наиболее разумной (например, Java final).

public:
     const int A;

Все хорошо, но если я хочу в обороне проверить это значение, как я могу go об этом. Приведенный ниже код кажется мне странным, но в отличие от Java final членов, я не могу установить A в конструкторе после защитных проверок (ошибка компилятора).

MyObj::MyObj(int a) : A(a) {
        if (a < 0)
            throw invalid_argument("must be positive");
    }

Публичная c константная переменная для A кажется более ясным и чистым решением, чем метод получения, только с неконстантным int за ним, но открытым для той или иной идеи, если это плохая практика.

Ответы [ 2 ]

2 голосов
/ 06 февраля 2020

Как правило, ответ Н. Шида является обычной практикой, но вы также можете рассмотреть следующие вопросы:

  1. Создание специфицированных для домена типов c и их использование. вместо общих примитивов. Например, если ваше поле является телефонным номером, имейте тип TelephoneNumber, который в своем конструкторе (или фабрике), принимая строку, выполняет всю проверку телефонного номера, которую вы хотите (и выдает недействительный). Затем вы напишите что-то вроде:

    class Contact {
      const TelephoneNumber phone_;
     public:
      Contact(string phone) : phone_(phone) { ... }
      ...
    

    Когда вы сделаете это, при инициализации поля phone_ будет вызван конструктор для TelephoneNumber со строковым аргументом, и произойдет проверка.

    Использование доменных спецификаций c типов таким образом обсуждается в Интернете под названием «примитивная одержимость» как «запах кода».

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

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

    string ValidatePhoneNumber(string v) {
      <some kind of validation throwing on invalid...>
      return v;
    }
    

    И используйте его следующим образом:

    class Contact {
      const string phone_;
     public:
      Contact(string phone) : phone_(ValidatePhoneNumber(phone)) { ... }
    

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

2 голосов
/ 06 февраля 2020

Ваш пример в его нынешнем виде должен работать нормально:

class MyObj {
public:
  const int var;
  MyObj(int var) : var(var) {
    if (var < 0)
       throw std::invalid_argument("must be positive");
  }
};

( Живой пример или с конструктором вне строки )

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

class MyObj {
  int var;
public:
  MyObj(int var) : var(var) {
    if (var < 0)
      throw std::invalid_argument("must be positive");
  }

  int getVar() const { return var; }
};

// now allows
MyObj a(5);
MyObj b(10);
a = b;

Edit

Очевидно, что вы хотите сделать что-то вроде

  MyObj(int var) {
    if (var < 0)
       throw std::invalid_argument("must be positive");
    this->var = var;
  }

Это невозможно; если переменная const имеет значение, ее нельзя изменить. Как только тело ({} бит) конструктора запускается, переменные const уже имеют значение, хотя в этом случае значение равно «undefined», так как вы его не устанавливаете (а компилятор выдает ошибку из-за это).

Более того, в этом нет никакого смысла. Нет никакой разницы в эффективности установки переменной после проверок или перед ними, и это не похоже на то, что какие-либо внешние наблюдатели смогут увидеть разницу независимо от того, как оператор throw развернет стек, сразу же деконструируя объект.

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