Берут ли ссылки память в C ++? - PullRequest
3 голосов
/ 13 января 2012

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

Может ли кто-нибудь объяснить, что произойдет для приведенного ниже примера. Как работает компилятор со ссылкой ri?

int main()
{  
    int *pi = new int(50);
    int &ri = *pi;
    ri = 30;
    cout << "val = " << ri << " , " << *pi << endl;
}

Это дает мне вывод:

val = 30  ,   30

Ответы [ 6 ]

3 голосов
/ 13 января 2012
int *pi = new int(50);

       +----+
pi --> | 50 |
       +----+


int &ri = *pi;

        +----+
pi  --> | 50 |    ri = 50, same as *pi, which is 50
        +----+

ri = 30;      now the contents of what pi points to i.e. *pi is replaced with 30

        +----+
pi  --> | 30 |
        +----+
2 голосов
/ 13 января 2012

Компилятор может заменить ссылку на фактический объект следующим образом:

int main()
{  
    int *pi = new int(50);
    //int &ri = *pi; //compiler might remove this

    *pi = 30; //then it replaces ri with *pi 

    cout << "val = " << *pi << " , " << *pi << endl; //here as well
}

Это то, что может сделать компилятор.

1 голос
/ 13 января 2012

Ссылки определяются как псевдонимы.Стандарт не определяет, как они представлены, хотя реализации не сильно различаются.По существу:

  • В общем случае ссылка - это скрытый адрес объекта (например, указатель)
  • Когда это возможно, компилятор будет стремиться исключитьindirection

Давайте посмотрим, как оно переводится, начиная с вашей программы:

int main()
{  
    int *pi = new int(50);
    int &ri = *pi;
    ri = 30;
    std::cout << "val = " << ri << " , " << *pi << std::endl;
}

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

int main()
{  
    int *pi = new int(50);
    *pi = 30;
    std::cout << "val = " << *pi << " , " << *pi << std::endl;
}

Мы можем исключить *pi, поскольку его окончательное значение известно компилятору:

int main() {
  new int(50); // stupid possible side effect usually forbid to optimize this out
  std::cout << "val = " << 30 << " , " << 30 << std::endl;
}

Я хотел бы отметить, что в вашем примере newВызов полностью бесполезен, вы также можете ссылаться на объекты, которые не были динамически распределены.

int main() {
  int i = 50;
  int& ri = i;
  ri = 30;
  std::cout << "val = " << ri << " < " << i << std::endl;
}

был одинаково действителен и без утечки памяти.


Возвращаясь к нашему различию междупредставления:

void swap(Foo& left, Foo& right);

обычно реализуются в виде:

void swap(Foo* left, Foo* right);

В этом случае ссылка в итоге занимает (некоторое) пространство (столько же, сколько указатель).

С другой стороны:

class Object {
public:
  Object(): foo(f), f() {}

  Foo const& foo;

  void set(Foo const& value);

private:
  Foo f;
};

Компилятор обычно не дает foo представление времени выполнения.Тот факт, что это ссылка const, будет использоваться для ограничения возможных методов, вызываемых на f, теми, кто не изменяет его (семантическая разница), но во время выполнения они будут напрямую передаваться f.

1 голос
/ 13 января 2012
int *pi = new int(50);

Вы выделяете объект int 50;

int &ri = *pi;

Вы устанавливаете псевдоним ri для этого объекта int, ri - это объект, а pi - адрес объекта;

ri = 30;

переназначить 30 на объект int;помните, что ri - это объект int;

cout << "val = " << ri << " , " << *pi << endl;

ri и * pi - это один и тот же объект.Вы просто помните, что объект может иметь много псевдонимов, и использование любого из этих псевдонимов может манипулировать объектом.

и где находится удаление.

0 голосов
/ 20 июля 2015

Ссылочная переменная занимает память в стеке . Лучший способ проверить это - использовать сборку программы на c ++, как показано ниже .... написать программу на С ++, как показано ниже, и сгенерировать ее сборку

#include<iostream>
using namespace std;

int main(){
int i = 100;
int j = 200;
int &x1 = i;
int &x2 = i;
int &x3 = i;
int &x4 = i;
int &x5 = i;
int &x6 = i;
int &x7 = i;

cout << "reference value" << x1 << endl;
return 0;

}

Сборка этой программы показывает, что указатель стека перемещен на 36 байт ", что означает, что ссылка получает память".

main:
.LFB966:
.cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl   %ebx
    andl    $-16, %esp
    subl    $64, %esp   
    movl    $100, 28(%esp)

Выше инструкция (subl $ 64,% esp) - это перемещение указателя стека для выделения места для i, j и семи ссылочных переменных (от x1 до x7) в стеке.

Если вы измените вышеуказанную программу cpp, как показано ниже

#include<iostream>
using namespace std;

int main(){
int i = 100;
int j = 200;
int &x1 = i;
cout << "ref changed" << x1 << endl; 
return 0;
}

Сборка, показывающая перемещение указателя стека на 12 байтов.

main:
.LFB966:
   .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl   %ebx
    andl    $-16, %esp
    subl    $32, %esp 
    movl    $100, 20(%esp)

Выше инструкции «subl $ 32,% esp» - перемещение указателя стека для выделения места для ссылочной переменной i, j и x1 в стеке

Я верю, что выше практическое очищает все, Поправь меня, если я ошибаюсь ..:)

0 голосов
/ 13 января 2012

ri - это переменная стека, которая работает так же, как указатель, который нельзя назначить новому адресу памяти. Это в основном семантический сахар, вы ничего не можете сделать со ссылкой, которую вы не можете сделать с указателем, вы можете просто сделать это более безопасно. Когда эта функция завершается, пространство, используемое ri, будет очищено от стека.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...