Что делает "&" в декларации С++?
Я парень C, и я пытаюсь понять некоторый код на С++. У меня есть следующее объявление функции:
int foo(const string &myname) {
cout << "called foo for: " << myname << endl;
return 0;
}
Каким образом сигнатура функции отличается от эквивалентной C:
int foo(const char *myname)
Есть ли разница между использованием string *myname
vs string &myname
? В чем разница между &
в С++ и *
в C, чтобы указать указатели?
Аналогично:
const string &GetMethodName() { ... }
Что здесь делает &
? Есть ли какой-нибудь сайт, объясняющий, как &
используется по-разному в C vs С++?
Ответы
Ответ 1
"&" обозначает ссылку вместо указателя на объект (в вашем случае постоянная ссылка).
Преимущество такой функции, как
foo(string const& myname)
над
foo(string const* myname)
заключается в том, что в первом случае вам гарантировано, что myname не является нулевым, поскольку С++ не разрешает ссылки NULL. Поскольку вы передаете по ссылке, объект не копируется, как если бы вы проходили указатель.
Второй пример:
const string &GetMethodName() { ... }
Позволит вам вернуть постоянную ссылку, например, на переменную-член. Это полезно, если вы не хотите, чтобы копия была возвращена, и снова гарантируется, что возвращаемое значение не является нулевым. Например, следующее позволяет вам осуществлять прямой доступ только для чтения:
class A
{
public:
int bar() const {return someValue;}
//Big, expensive to copy class
}
class B
{
public:
A const& getA() { return mA;}
private:
A mA;
}
void someFunction()
{
B b = B();
//Access A, ability to call const functions on A
//No need to check for null, since reference is guaranteed to be valid.
int value = b.getA().bar();
}
Конечно, вы должны быть осторожны, чтобы не возвращать недействительные ссылки.
Компиляторы с радостью скомпилируют следующее (в зависимости от вашего уровня предупреждения и того, как вы обрабатываете предупреждения)
int const& foo()
{
int a;
//This is very bad, returning reference to something on the stack. This will
//crash at runtime.
return a;
}
В принципе, вы несете ответственность за то, чтобы все, что вы возвращаете ссылку, действительно действительно.
Ответ 2
Здесь &
не используется как оператор. В качестве части объявлений функций или переменных &
обозначает ссылку. В С++ FAQ Lite есть довольно красивая глава по ссылкам.
Ответ 3
string * и string & отличаются несколькими способами. Прежде всего, указатель указывает на местоположение адреса данных. Ссылка указывает на данные. Если у вас была следующая функция:
int foo(string *param1);
Вам нужно будет проверить объявление функции, чтобы убедиться, что param1 указал на допустимое местоположение. Для сравнения:
int foo(string ¶m1);
Здесь ответственность за звонящего должна удостовериться, что указанные данные действительны. Вы не можете передать значение "NULL", например, вторую вторую функцию.
Что касается вашего второго вопроса, о возвращаемых значения метода, являющихся эталоном, рассмотрим следующие три функции:
string &foo();
string *foo();
string foo();
В первом случае вы возвращаете ссылку на данные. Если объявление вашей функции выглядело так:
string &foo()
{
string localString = "Hello!";
return localString;
}
Вероятно, вы получите некоторые ошибки компилятора, так как вы возвращаете ссылку на строку, которая была инициализирована в стеке для этой функции. По возврату функции это местоположение данных больше не действует. Как правило, вы хотели бы вернуть ссылку на члена класса или что-то в этом роде.
Вторая функция выше возвращает указатель в фактической памяти, поэтому она останется прежней. Однако вам нужно будет проверить NULL-указатели.
Наконец, в третьем случае возвращаемые данные будут скопированы в возвращаемое значение для вызывающего. Поэтому, если ваша функция была такой:
string foo()
{
string localString = "Hello!";
return localString;
}
У вас все будет хорошо, так как строка "Hello" будет скопирована в возвращаемое значение для этой функции, доступное в памяти памяти вызывающего абонента.
Ответ 4
Один из способов взглянуть на оператор (reference) в С++ - это просто синтаксический сахар для указателя. Например, следующие примерно эквивалентны:
void foo(int &x)
{
x = x + 1;
}
void foo(int *x)
{
*x = *x + 1;
}
Более полезно, когда вы имеете дело с классом, чтобы ваши методы превратились из x- > bar() в x.bar().
Причина, по которой я грубо говорю, заключается в том, что использование ссылок накладывает дополнительные ограничения времени компиляции на то, что вы можете сделать с ссылкой, чтобы защитить вас от некоторых проблем, возникающих при работе с указателями. Например, вы не можете случайно изменить указатель или использовать указатель любым способом, кроме ссылки на выделенный объект, который вы передали.
Ответ 5
#include<iostream>
using namespace std;
int add(int &number);
int main ()
{
int number;
int result;
number=5;
cout << "The value of the variable number before calling the function : " << number << endl;
result=add(&number);
cout << "The value of the variable number after the function is returned : " << number << endl;
cout << "The value of result : " << result << endl;
return(0);
}
int add(int &p)
{
*p=*p+100;
return(*p);
}
Это недопустимый код для нескольких счетчиков. Запуск через g++ дает:
crap.cpp: In function ‘int main()’:
crap.cpp:11: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int*’
crap.cpp:3: error: in passing argument 1 of ‘int add(int&)’
crap.cpp: In function ‘int add(int&)’:
crap.cpp:19: error: invalid type argument of ‘unary *’
crap.cpp:19: error: invalid type argument of ‘unary *’
crap.cpp:20: error: invalid type argument of ‘unary *’
Действительная версия кода:
#include<iostream>
using namespace std;
int add(int &number);
int main ()
{
int number;
int result;
number=5;
cout << "The value of the variable number before calling the function : " << number << endl;
result=add(number);
cout << "The value of the variable number after the function is returned : " << number << endl;
cout << "The value of result : " << result << endl;
return(0);
}
int add(int &p)
{
p=p+100;
return p;
}
Что здесь происходит, так это то, что вы передаете переменную "как есть" в вашу функцию. Это примерно эквивалентно:
int add(int *p)
{
*p=*p+100;
return *p;
}
Однако передача ссылки на функцию гарантирует, что вы не можете делать такие вещи, как арифметика указателя со ссылкой. Например:
int add(int &p)
{
*p=*p+100;
return p;
}
неверно.
Если вы должны использовать указатель на ссылку, это должно быть сделано явно:
int add(int &p)
{
int* i = &p;
i=i+100L;
return *i;
}
Что на тестовом прогоне дает (как ожидалось) выход мусора:
The value of the variable number before calling the function : 5
The value of the variable number after the function is returned : 5
The value of result : 1399090792
Ответ 6
Ваша функция объявляет постоянную ссылку на строку:
int foo(const string &myname) {
cout << "called foo for: " << myname << endl;
return 0;
}
Ссылка имеет некоторые специальные свойства, которые делают ее более безопасной альтернативой указателям разными способами:
- он никогда не может быть NULL
- он всегда должен быть инициализирован
- его нельзя изменить, чтобы ссылаться на другую переменную после установки
- он может использоваться точно так же, как и переменная, к которой он относится (что означает, что вам не нужно относиться к нему как к указателю)
Каким образом сигнатура функции отличается от эквивалентной C:
int foo(const char *myname)
Существует несколько отличий, поскольку первый относится непосредственно к объекту, а const char*
должен быть разыменован, чтобы указывать на данные.
Есть ли разница между использованием строки * myname vs string & myname?
Основное отличие при работе с параметрами заключается в том, что вам не нужно разыгрывать &myname
. Более простой пример:
int add_ptr(int *x, int* y)
{
return *x + *y;
}
int add_ref(int &x, int &y)
{
return x + y;
}
которые делают то же самое. Единственное отличие в этом случае заключается в том, что вам не нужно разыгрывать x
и y
, поскольку они ссылаются непосредственно на переданные переменные.
const string &GetMethodName() { ... }
Что здесь и что делает? Есть ли какой-то сайт, который объясняет, как и используется по-разному в C vs С++?
Это возвращает постоянную ссылку на строку. Таким образом, вызывающий абонент получает доступ к возвращенной переменной напрямую, но только в смысле только для чтения. Это иногда используется для возврата членов строковых данных без выделения дополнительной памяти.
Есть несколько тонкостей со ссылками - посмотрите на С++ FAQ на ссылках для получения более подробной информации.
Ответ 7
В этом контексте &
вызывает функцию stringname
по ссылке.
Разница между ссылками и указателями такова:
- Когда вы берете ссылку на переменную, эта ссылка является переменной, на которую вы ссылались. Вам не нужно разыгрывать его или что-то еще, работа с эталонным семантически равна работе с самой ссылочной переменной.
-
NULL
не является допустимым значением для ссылки и приведет к ошибке компилятора. Поэтому, как правило, если вы хотите использовать выходной параметр (или указатель/ссылку в целом) в функции С++, и передать значение null этому параметру, следует использовать указатель (или интеллектуальный указатель, желательно). Если передача нулевого значения не имеет смысла для этой функции, используйте ссылку.
- Вы не можете "переустановить" ссылку. Хотя значение указателя можно изменить, чтобы указать на что-то другое, ссылка не имеет аналогичной функциональности. Как только вы берете переменную по ссылке, вы эффективно имеете дело с этой переменной напрямую. Так же, как вы не можете изменить значение
a
, написав b = 4;
. Контрольное значение - это значение того, на что оно ссылалось.