Подтвердить что ты не робот

Что делает "&" в декларации С++?

Я парень 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 С++?

4b9b3361

Ответ 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 &param1);

Здесь ответственность за звонящего должна удостовериться, что указанные данные действительны. Вы не можете передать значение "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;. Контрольное значение - это значение того, на что оно ссылалось.