Рекомендуемое использование ссылочного типа С++

Я программирую на С++ более 5 лет и никогда не встречал места, где рекомендуется ссылаться на переменную, кроме как аргумент функции (если вы не хотите копировать то, что вы передаете в качестве аргумента функции), Так может ли кто-то указывать случаи, когда рекомендуется ссылаться на ссылку на С++ (я имею в виду, что это дает какое-либо преимущество).

Ответ 1

В качестве возвращаемого значения непрозрачного набора/мутатора коллекции

operator[] std::map возвращает ссылку.


Чтобы сократить текст, необходимый для ссылки на переменную

Если вы пропустите инструкцию старой школы with Foo do ... (этот синтаксис Pascal), вы можете написать

 MyString &name = a->very->long_->accessor->to->member;
 if (name.upcase() == "JOHN") {
    name += " Smith";
 }

еще один пример этого можно найти в Ответ Майка Данлави


Чтобы указать, что что-то является ссылкой

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

Пример:

class User_Filter{
  std::list<User> const& stop_list;
  public: Functor (std::list<User> const& lst)
    : stop_list(lst) { }
  public: bool operator()(User const& u) const
    {  return stop_list.exists(u); }
};

find_if(x.begin(),x.end(),User_Filter(user_list));

Идея здесь в том, что это ошибка компиляции, если вы не инициализируете ссылку в конструкторе такого объекта. Чем больше проверок во время компиляции, тем лучше программы.

Ответ 2

Здесь случай, когда он удобен:

MyClass myArray[N];

for (int i = 0; i < N; i++){
    MyClass& a = myArray[i];
    // in code here, use a instead of myArray[i], i.e.
    a.Member = Value;
}

Ответ 3

Используйте ссылки везде, где хотите, указатели, когда вы вынуждены.

Ссылки и указатели разделяют часть их семантики: они являются псевдонимом элемента, которого нет. Основное различие заключается в управлении памятью: ссылки четко выражают, что вы не отвечаете за ресурс. С другой стороны, с указателями это действительно не ясно (если вы не имеете в виду интеллектуальные указатели): предполагается ли, что вы удалите указатель или удалите его извне?

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

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

Теперь, когда вы указываете, они действительно не нужны: вы всегда можете использовать указатели для всех использований ссылок (даже для передачи параметров), но тот факт, что вы можете использовать один инструмент для всего, не означает, что нет более подходящие инструменты для работы.

Ответ 4

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

EDIT (добавленный пример):

Скажем, что у вас есть база данных и класс DAO с базой данных как зависимость:

struct Database {};
struct PersonDao {
    const Database &m_d;
    PersonDao(const Database &d): m_d(d) {}
};

Кроме того, объем базы данных контролируется извне из DAO:

int main() {
    Database d;
    PersonDao pd(d);
}   

В этом случае имеет смысл использовать ссылочный тип, поскольку вы никогда не хотите, чтобы DAO:: m_d был нулевым, а его время жизни контролируется извне (от основной функции в этом случае).

Ответ 5

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

... и сделать это абсолютно ясно (- > комментарии). Я склонен избегать указателей на модель "возможно, есть несколько значений" - вектор является лучшим вариантом здесь. Указатели на несколько значений часто заканчиваются программированием на языке C, потому что вы обычно должны передавать # элементов, а также отдельно.

Ответ 6

Используйте ссылку const, чтобы указать имя для значения, например:

const Vec3 &ba=b-a;

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

(Связанный не дублируемый вопрос о переполнении стека в Ссылка на ссылку на временный. Ссылка Herb Sutter содержит дополнительную информацию об этом.)

Ответ 7

Аргумент конструктора-копии ДОЛЖЕН быть передан как ссылка, поскольку в противном случае конструктор копирования должен был бы вызвать его сам в бесконечной рекурсии (переполнение стека).

Ответ 8

Я склонен соглашаться, но, возможно, константа возвращает значения.

Ответ 9

У вас есть два варианта для сглаживания других значений (игнорирование shared_ptrs и т.п.): указатели и ссылки.

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

Возможно, что более важно, ссылки могут быть использованы без размышлений обо всех проблемах, возникающих с указателями. Это, вероятно, главное преимущество. Семантически ссылка - это вещь. Если вы гарантируете, что вызывающий/вызываемый, что базовая память не исчезнет, ​​вам не нужно путать пользователя с любыми вопросами, которые возникают вместе с указателями (нужно ли мне освобождать это? Может ли это быть NULL? и т.д.) и может безопасно использовать ссылку для удобства.

Примером этого может быть функция, которая ищет соответствующую строку для перечисления,

const std::string& ConvertToString( someEnum val)
{
   static std::vector< std::string > lookupTable;
   if (lookupTable.empty())
   {
      // fill in lookup table
   }
   // ignoring the cast that would need to happen
   return lookupTable[val]
}

Здесь договор между вызывающим и вызываемым лицами гарантирует, что тип возврата всегда будет там. Вы можете безопасно вернуть ссылку и избежать некоторых вопросов, которые приглашают указатели.

Ответ 10

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

Ответ 11

Операторы потоков являются очевидным примером

std::ostream & operator<< (std::ostream &, MyClass const &...) {
  ....
}

mystream << myClassVariable;

Вам явно не нужен указатель, так как проверка NULL делает использование оператора очень утомительным i.s.o. удобно

Ответ 12

Я хотел бы заручиться некоторыми случаями:

1) при написании одноэлементных классов

 class singleton
    {
        singleton();
        explicit singleton(const singleton&);
        singleton& operator=(const singleton&);
    public:
        static singleton& instance()
        {
            static singleton inst;
            return inst;
        }
    };// this is called the 'Meyers' singleton pattern. refer to More Effective C++ by  Scott Meyers

он имеет все преимущества, но избегает использования нового оператора

** 2) ** здесь нет такой вещи, как пустая ссылка. Ссылка должна всегда ссылаться на какой-либо объект. В результате, если у вас есть переменная, целью которой является обращение к другому объекту, но возможно, что не может быть объекта для ссылки, вы должны сделать указатель переменной, потому что тогда вы можете установить его в null. С другой стороны, если переменная всегда должна ссылаться на объект, т.е. Если ваша конструкция не допускает, чтобы переменная была нулевой, вероятно, вы должны сделать переменную ссылкой

** 3) ** Поскольку ссылка должна ссылаться на объект, С++ требует, чтобы ссылки были инициализированы:

  string& rs;             // error! References must
                          // be initialized

  string s("xyzzy");

  string& rs = s;         // okay, rs refers to s

Pointers are subject to no such restriction

Тот факт, что нет такой вещи, как нулевая ссылка, означает, что более эффективно использовать ссылки, чем использовать указатели. Это потому, что нет необходимости проверять достоверность ссылки перед ее использованием.

** 4) ** Еще одно важное различие между указателями и ссылками состоит в том, что указатели могут переназначаться для обозначения разных объектов. Ссылка, однако, всегда относится к объекту, с которым он инициализируется: ¤ Пункт M1, P10

  string s1("Nancy");
  string s2("Clancy");

  string& rs = s1;         // rs refers to s1

  string *ps = &s1;        // ps points to s1

  rs = s2;                 // rs still refers to s1,
                           // but s1 value is now
                           // "Clancy"

  ps = &s2;                // ps now points to s2;
                           // s1 is unchanged

Ответ 13

Я использовал ссылку на ostream вместо указателя. Я полагаю, что предпочитаю ссылки на указатели, когда класс имеет много операторов.