Передача по ссылке на конструктор - PullRequest
24 голосов
/ 01 марта 2012

Я решил посмотреть, сделает ли назначение ссылки для члена ссылкой.Я написал следующий фрагмент, чтобы проверить это.Есть простой класс Wrapper с std::string в качестве переменной-члена.Я беру const string& в конструкторе и присваиваю его переменной-члену public.Позже в методе main() я изменяю переменную-член, но string, который я передал конструктору, остается неизменным, как получилось?Я думаю, что в Java переменная изменилась бы, почему бы не в этом фрагменте кода?Как именно работают ссылки в этом случае?

#include <iostream>
#include <string>
using namespace std;

class Wrapper
{
public:
   string str;

   Wrapper(const string& newStr)
   {
      str = newStr;
   }
};

int main (int argc, char * const argv[]) 
{
   string str = "hello";
   cout << str << endl;
   Wrapper wrapper(str);
   wrapper.str[0] = 'j'; // should change 'hello' to 'jello'
   cout << str << endl;
}

Ответы [ 6 ]

30 голосов
/ 01 марта 2012

Чтобы назначить ссылку в конструкторе, вам нужно иметь элемент ссылки

 class A{
     std::string& str;
 public:
     A(std::string& str_)
     :    str(str_) {} 
 };

Теперь str является ссылкой на переданное вами значение. То же самое относится к константным ссылкам

 class A{
     const std::string& str;
 public:
     A(const std::string& str_)
     :    str(str_) {} 
 };

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

7 голосов
/ 01 марта 2012

Поскольку Wrapper::str не является ссылкой, это независимый объект.Поэтому, когда вы делаете str = newStr, вы копируете строку.

5 голосов
/ 01 марта 2012
class Wrapper
{
public:
   string& str;

   Wrapper(string& newStr) : str(newStr) {}
};

Обратите внимание: вы не можете принять const string& и сохранить его в string&, при этом вы потеряете константность.

3 голосов
/ 01 марта 2012

Вам нужно использовать инициализатор и объявить str в качестве ссылки, как в:

class Wrapper {
public:
   string &str;

   Wrapper(string& newStr)
      : str(newStr) {
   }
};

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

1 голос
/ 01 апреля 2016

Ваша основная переменная тела - std::string.

Ваша переменная параметра const std::string&.

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

Напротив, «верхний уровень const» изменяет реальный объект. Прочитайте C ++ Primer на верхнем уровне const для уточнения.

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

const std::string& = std::str; //Values are ommited
// i.e const std::string newStr = std::string str

Вы инициализируете const type reference с non-const value, что является приемлемым. Вы не должны изменять значение std::string str, используя эту ссылку. И, если вы попытаетесь изменить значение newStr внутри конструктора, вы получите ошибку компиляции .

Затем вы делаете другое назначение внутри конструктора, что также приемлемо:

std::string = const std::string 

Тот факт, что wrap.str[0] не изменил str из main, заключается в том, что хотя ссылка использовалась для создания экземпляра class str, class str имеет собственный объект и не связана с main str. Использование этой ссылки в параметре просто связывает этот параметр с main str; не main str до class str.

Если бы на ваши переменные класса ссылались, то это могло бы измениться.

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

Вы должны объявить Wrapper::str как string&, а не как string.

...