C ++ эквивалент для данных финального члена Java - PullRequest
5 голосов
/ 01 марта 2010

Во-первых, мое последнее кодирование - это Java, и я не хочу «писать Java на C ++».

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

Так, каков наилучший способ создания такого класса? И как я могу представить свои неизменные / окончательные свойства внешнему миру в стандартах C ++?

вот пример класса:

class Msg {
    private:
        int _rec_num;
        int _seq;
        string text;
    public:
        Msg(const char* buffer) {
            // parse the buffer and get our member here...
            // ... lots of code
        }

        // does this look like proper C++?
        int get_rec_num() { return _rec_num; }
    };

Ответы [ 7 ]

8 голосов
/ 01 марта 2010

C ++ предлагает несколько приятных механизмов, позволяющих сделать ваш класс неизменным. Что вы должны сделать, это:

  • объявить все ваши публичные (и, возможно, защищенные) методы const
  • объявить (но не определить) operator= как личное

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

int get_rec_num() const { return _rec_num; }

РЕДАКТИРОВАТЬ : Начиная с C++11 вы можете явно удалить operator= вместо того, чтобы просто оставить его неопределенным. Это явно указывает компилятору не определять оператор назначения копирования по умолчанию:

Msg& operator=(const Msg&) = delete;
3 голосов
/ 01 марта 2010

Я бы пометил ваш неизменный член как 'const' и присвоил бы ему значение в вашем списке инициализатора конструктора.

Я бы также проанализировал ваш буфер вне класса и передал бы строку в конструктор.

Примерно так:

class Msg {
  private:
    int _rec_num;
    int _seq;
    const std::string text;
  public:
    Msg(const std::string& str) :
      text(str)
    {

    }

    // does this look like proper C++?
    int get_rec_num() const { return _rec_num; }
};

// parse the buffer and get our parameter somewhere else

Примечание:

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

Вы должны избегать включения использования std :: string в заголовочные файлы; поскольку любой, кто включает ваш заголовок, навязывает им это «использование».

1 голос
/ 01 марта 2010
    // does this look like proper C++?
    int get_rec_num() { return _rec_num; }

Вы должны использовать

    int get_rec_num() const { return _rec_num; }

(см. const, который позволяет вызывать член для константных объектов).

1 голос
/ 01 марта 2010

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

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

0 голосов
/ 02 марта 2010

Прежде всего, можно эффективно инициализировать элементы во время сборки, даже если они были объявлены как const (что не является ни необходимым, ни рекомендуемым).

Я бы все же предложил разделить этот класс на два отдельных класса (псевдокод):

// Msg: pure data object; copy constructible but not modifiable.
class Msg
{
public:
  Msg(int rec_num, ...)
    : rec_num_(rec_num)
    ...
  {}

  int rec_num() const
  { return rec_num_; }
  ...
private:
  // prevent copying
  Msg& operator=(Msg const&);
private:
  int rec_num_;
}; 

// MsgParser: responsible for parsing the buffer and
// manufacturing Msg's.
class MsgParser
{
public:
  static Msg Parse(char const* buffer)
  {
     ... parse ...
     return Msg(...);
  }
};

// Usage
Msg const msg = MsgParser::Parse(buffer);

Это также хорошо разделяет задачи хранения и анализа данных на отдельные классы.

0 голосов
/ 01 марта 2010

Чтобы сделать переменную неизменной, вы должны использовать ключевое слово const, например, const int _rec_num. Константные переменные могут быть инициализированы только через список инициализации , который вызывается перед любым кодом в конструкторе. Это означает, что вы не можете выполнять какую-либо обработку в конструкторе, который устанавливает переменные-члены const.

У вас есть два способа обойти это, во-первых, вы можете создать другой внутренний класс, который принимает буфер и анализирует его в ваших переменных. Поместите const версию этого в ваш класс MSG и поместите в список инициализации:

class MsgInner
{
    public:
    int _rec_num;

    Msg(const char* buffer) {
        // Your parsing code
    }
};

class Msg
{
    public:
    const MsgInner msg;

    Msg(const char* buffer) : msg(buffer)
    { // any other code }
};

Возможно, это не так "стандартно", но это другая перспектива. В противном случае вы также можете сделать это, как предложили другие ответы с помощью методов get.

0 голосов
/ 01 марта 2010

В финализаторах

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

Об неизменяемости и инициализации

Как правило, если что-то является неизменным и const-correct , класс будет иметь все свои члены как const, и единственный раз, когда вы можете "записать" их, - это когда класс инициализирован. Однако в вашем случае это может оказаться невозможным.

Я предлагаю вам собрать все свои ресурсы и инициализировать класс (через конструктор не по умолчанию с константными членами), как только они у вас появятся. Другая альтернатива (которой я не придерживаюсь) - это иметь Функция-мутатор, которая «претендует» на правильность const, но записывает в значения const единовременную инициализацию после построения.

...