Getter и setter, указатели или ссылки и хороший синтаксис для использования в С++?

Я хотел бы знать хороший синтаксис для получателей и сеттеров С++.

private:
YourClass *pMember;

setter легко, я думаю:

void Member(YourClass *value){
  this->pMember = value; // forget about deleting etc
}

и геттер? следует ли использовать ссылки или константные указатели?

Пример:

YourClass &Member(){
   return *this->pMember;
}

или

YourClass *Member() const{
  return this->member;
}

В чем разница между ними?

Спасибо,

Джо

EDIT:

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

поэтому я предполагаю, что буду использовать указатели const вместо ссылок

Указатели const не могут быть удалены или установлены, правильно?

Ответ 1

Как общий закон:

  • Если NULL является допустимым параметром или возвращаемым значением, используйте указатели.
  • Если NULL НЕ действительный параметр или возвращаемое значение, используйте ссылки.

Итак, если установщик должен быть вызван с помощью NULL, используйте указатель в качестве параметра. В противном случае используйте ссылку.

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

Ответ 2

Лучше всего предоставить реальный интерфейс OO для клиента, который скрывает детали реализации. Getters и Setters не являются OO.

Ответ 3

Ваш код выглядит так, как будто вы привыкли к другому языку - в С++ с использованием this->x (для примера) относительно необычно. Когда код полностью написан, поэтому используется аксессор или мутатор.

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

Когда/если вам нужно контролировать, какое значение назначено, перегрузка оператора позволяет вам взять этот элемент управления, не вызывая уродливого синтаксиса get/set на клиентском коде. В частности, вам нужен прокси-класс (или шаблон класса). Например, одна из наиболее распространенных ситуаций, когда люди хотят получать/устанавливать функции, - это нечто вроде числа, которое должно быть ограничено определенным диапазоном. setXXX проверяет новое значение для нахождения в диапазоне, а getXXX возвращает значение.

Если вы этого хотите, (справедливый) простой шаблон может сделать работу намного чище:

template <class T, class less=std::less<T> >
class bounded {
    const T lower_, upper_;
    T val_;

    bool check(T const &value) {
        return less()(value, lower_) || less()(upper_, value);
    }

    void assign(T const &value) {
        if (check(value))
            throw std::domain_error("Out of Range");
        val_ = value;
    }

public:
    bounded(T const &lower, T const &upper) 
        : lower_(lower), upper_(upper) {}

    bounded(bounded const &init) 
        : lower_(init.lower), upper_(init.upper)
    { 
        assign(init); 
    }

    bounded &operator=(T const &v) { assign(v);  return *this; }

    operator T() const { return val_; }

    friend std::istream &operator>>(std::istream &is, bounded &b) {
        T temp;
        is >> temp;

        if (b.check(temp))
            is.setstate(std::ios::failbit);
        else
            b.val_ = temp;
        return is;
    }
};

Это также делает код намного ближе к самодокументированию - например, когда вы объявляете объект вроде: bounded<int>(1, 1024);, сразу видно, что цель является целым числом в диапазоне от 1 до 1024. Единственная часть - кто-то может найти открытым вопрос, входит ли 1 и/или 1024 в диапазон. Это значительно отличается от определения int в классе и ожидает, что все, кто когда-либо смотрит на класс, понимают, что они должны использовать setXXX для принудительного применения некоторого (в этой точке неизвестного) набора границ значений, которые могут быть назначен.

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

Ответ 4

Как говорили другие, используйте указатели, если null является возможностью.

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

(И да, я обычно использую интеллектуальные указатели. Это похоже на ссылки в моем сознании. Я говорю о коде нижнего уровня, чем здесь.)

Ответ 5

В чем разница между ними?

Ссылка - это псевдоним вещи (это вещь *). Указатель - это адрес вещи. Если есть шанс, что то, что указано, не будет там, тогда вы, вероятно, не захотите возвращать ссылки. Ссылки говорят абоненту "Я собираюсь дать вам псевдоним, который будет существовать, когда я верну его вам". На самом деле, действительно нет способа проверить ссылку, чтобы увидеть, действительно ли то, что является основополагающим.

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

В конечном счете нет "правильного" ответа. Это зависит от договора класса, и если вызывающий должен/должен/хочет/хочет проверить, находится ли "член".

Короткий ответ - это указатели на вещи, которые можно указать в другом месте, а также ссылки на псевдонимы "unseated".

Ответ 6

+1 при опросе использования сеттеров и геттеров. Если вы должны использовать их и иметь возможность nulls рассмотреть использование boost:: shared_ptr. Таким образом, право собственности обрабатывается для вас.

Ответ 7

В дополнение к другим ответам, если вы выбираете ссылки для получателя, не пишите его, как в вашем примере:

YourClass &Member(){
   return *this->pMember;
}

Ваш геттер фактически разрешает установку, как в instance->Member() = YourClass();, и, таким образом, обходя ваш сеттер. Это может быть запрещено, если YourClass не подлежит копированию, но это еще одна вещь, о которой нужно помнить. Другим недостатком является то, что геттер не является константой.

Вместо этого напишите свой получатель следующим образом:

const YourClass &Member() const {
   return *this->pMember;
}

Ответ 8

Джонатан, какой компилятор вы используете? Там большой шанс, что shared_ptr уже поставляется с ним как часть реализации компилятора TR1.