Передача по ссылке на конструктор
Я решил посмотреть, сделает ли назначение ссылки для члена ссылкой. Я написал следующий фрагмент, чтобы проверить это. Там простой класс Wrapper
с std::string
как переменная-член. Я беру взять const string&
в конструкторе и присвойте его общедоступной переменной-члену. Позже в 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 ответов
Чтобы назначить ссылку в конструкторе, вам нужно иметь элемент ссылки
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, то вместо этого он должен быть указателем.
Так как Wrapper::str
это не ссылка, это независимый объект. Итак, когда вы делаете str = newStr
копируешь строку.
class Wrapper
{
public:
string& str;
Wrapper(string& newStr) : str(newStr) {}
};
Обратите внимание, вы не можете принять const string&
и хранить его в string&
Вы бы потеряли константность в этом.
Вам нужно использовать инициализатор и объявить str
как ссылка, как в:
class Wrapper {
public:
string &str;
Wrapper(string& newStr)
: str(newStr) {
}
};
То, как вы пишете, все, что вы делаете, это копируете значение ссылки, которую вы передаете конструктору. Вы не сохраняете ссылку. Объявив ссылку в качестве члена класса и инициализировав ее ссылкой на другой экземпляр строки, вы получите искомое поведение.
Ваша основная переменная тела std::string
,
Ваша переменная параметра const std::string&
,
Const в ссылках всегда является "низкоуровневым const", то есть он изменяет тип объекта, а не фактический объект.
Напротив, "верхний уровень 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
,
Если бы на ваши переменные класса ссылались, то это могло бы измениться.