Почему я не могу хранить ссылки в `std :: map` на C ++? - PullRequest
38 голосов
/ 09 октября 2009

Я понимаю, что ссылки - это не указатели, а псевдоним объекта. Тем не менее, я до сих пор не понимаю, что именно это значит для меня, как программиста, то есть, что такое ссылки под капотом?

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

Я знаю, что мне нужно перестать думать о ссылках как о синтаксическом шугере над указателями, просто не знаю, как: /

Ответы [ 6 ]

27 голосов
/ 09 октября 2009

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

22 голосов
/ 09 октября 2009

Вы должны рассматривать ссылку как «постоянный указатель на неконстантный объект»:

MyObject& ~~ MyObject * const

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

// Falsifying a reference
MyObject& firstProblem = *((MyObject*)0);
firstProblem.do(); // undefined behavior

// Referencing something that exists no more
MyObject* anObject = new MyObject;
MyObject& secondProblem = *anObject;
delete anObject;
secondProblem.do(); // undefined behavior

Теперь существует два требования к контейнеру STL:

  • T должен быть конструируемым по умолчанию (ссылка не является)
  • T должен быть назначен (вы не можете сбросить ссылку, хотя вы можете назначить ее рефери)

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

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

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

Надеюсь, это поможет:)

7 голосов
/ 09 октября 2009

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

В качестве иллюстрации этого:

A anObject, anotherObject;
A *pointerToA=&anObject;
A &referenceToA=anObject;

// We can change pointerToA so that it points to a different object
pointerToA=&anotherObject;

// But it is not possible to change what referenceToA points to.
// The following code might look as if it does this... but in fact,
// it assigns anotherObject to whatever referenceToA is referring to.
referenceToA=anotherObject;
// Has the same effect as
// anObject=anotherObject;
3 голосов
/ 08 мая 2016

на самом деле вы можете использовать ссылки на карте. я не рекомендую это для больших проектов, так как это может вызвать странные ошибки компиляции, но:

    map<int, int&> no_prob;
    int refered = 666;
    no_prob.insert(std::pair<int, int&>(0, refered)); // works
    no_prob[5] = 777; //wont compile!!! 
    //builds default for 5 then assings which is a problem
    std::cout << no_prob[0] << std::endl; //still a problem
    std::cout << no_prob.at(0) << std::endl; //works!!

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

1 голос
/ 17 июня 2014

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

struct container
{
   string& s_;           // string reference
};

int main()
{
   string s { "hello" };
   //container {};       // error - object has an uninitialized reference member
   container c { s };    // Ok
   c.s_ = "bye";
   cout << s;            // prints bye
}

Кроме того, после инициализации хранилище для элементов контейнера нельзя изменить. s_ будет всегда обращаться к хранилищу s выше.

0 голосов
/ 13 сентября 2011

Этот пост объясняет, как указатели реализованы под капотом - http://www.codeproject.com/KB/cpp/References_in_c__.aspx,, который также поддерживает ответ Себастьян.

...