2012-01-13 4 views
3

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

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

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

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

val = 30 , 30 
+0

Никакие ссылки не потребляют никакой памяти, и любые изменения, внесенные в переменную или ссылку, отражаются друг к другу. Это единственное, что вам нужно знать. Все, что компилятор заботится, и вам не нужно беспокоиться около. –

+1

Лучше всего рассматривать ссылки как несколько иной синтаксис для указателей. Спецификация ссылок на C++ такова, что, хотя технически это не указатели, вряд ли компилятор мог бы рассматривать их как что-либо, кроме указателей, и по-прежнему быть эффективным. _Небольшая обработка немного отличается, но опять же, только логически, компилятор вряд ли что-то сделает. –

ответ

2

Компилятор может заменить ссылку с реальным объектом, как:

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 
} 

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

+0

Что происходит с передачей функции по ссылке? например swap (int & a, int & b) вызывает как swap (i, j) и swap (x, y). Рассмотрим, что i, j, x, y все являются целыми. –

+0

Это лучшее объяснение, которое я вижу. Int & ri = * pi; эквивалентно int * ri = pi; он просто дает вам «прямой» доступ к переменной без необходимости в звездочке. Обратите внимание, что ссылка может быть нулевой (т. Е. Если (& ri == NULL) может быть правдой.) –

+7

@AlexisWilke: нет, она не может быть нулевой (педантично). Чтобы создать такую ​​ссылку, вам нужно будет разыменовать нулевой указатель, который вызывает ** неопределенное поведение **. И в этот момент ваша программа перестает быть программой на C++, потому что она больше не подчиняется стандарту C++. –

3
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 | 
     +----+ 
0

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

1
int *pi = new int(50); 

Вы назначаете объект int 50;

int &ri = *pi; 

вы установили псевдоним ри этой Int объекта, ИО этот объект и пи это адрес объекта;

ri = 30; 

переназначить 30 на объект int; помните, что ri является объектом int;

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

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

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

1

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

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

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

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

Мы E liminate 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.

0

Справочная переменная принимает память на стеке, Лучший способ проверить это, используя сборку C++ программы, как показано ниже .... написать программу на 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,% особ) является стек перемещением указателя выделить место для 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,% ЭСП" стек указатель перемещение, чтобы выделить место для переменной i, j и x1 в стеке

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

Смежные вопросы