Ссылки и указатели тесно связаны между собой. Оба являются способами передачи параметров без копирования значения параметра в кадр стека подпрограммы.
Основное различие между ними:
- Указатель
p
указывает на объект o
.
- Ссылка
i
является объектом o
. Другими словами, в псевдониме.
Чтобы сделать вещи более запутанными, насколько я знаю, реализация компилятора между ними почти одинакова.
Представьте себе функции Ptr(const T* t)
и Ref(const T& t)
.
int main () {
int a;
Ptr (& а);
Ссылка (а);
}
В Ptr
, t
будет указывать на местоположение a
. Вы можете разыменовать его и получить значение a
. Если вы введете &t
(берете адрес t
), вы получите адрес параметра.
In Ref
, t
равно a
. Вы можете использовать a
для значения a
. Вы можете получить адрес a
с помощью &a
. Это маленький синтаксический сахар, который дает вам c ++.
Оба предоставляют механизм для передачи параметров без копирования. В вашей функции (кстати, вам не нужно объявление):
template <class T> void utilShow(T elem) { ... }
Каждый раз, когда его вызывают, T
будет копироваться. Если T большой вектор, он копирует все данные в векторе. Это довольно неэффективно. Вы не хотите передавать весь вектор в новый кадр стека, вы хотите сказать «эй - новый кадр стека, используйте эти данные». Таким образом, вы можете перейти по ссылке. Как это выглядит?
template <class T> void utilShow(const T &elem) { ... }
elem
равно const
, потому что оно не изменяется функцией. Он также будет использовать память для elem
, хранящуюся в вызывающей стороне, вместо того, чтобы копировать ее в стек.
Опять же, по той же причине (чтобы избежать копирования параметров), используйте:
vector< vector<short> > getMatrixFromFile(const string &fName) { ... }
void showMatrix(const vector< vector<short> > &mat) { ... }
Единственная сложность в том, что вы можете подумать: «Эй, ссылка означает отсутствие копий! Я буду использовать ее все время! Я буду возвращать ссылки из функций!» И вот где ваша программа падает.
Представьте себе это:
// Don't do this!
Foo& BrokenReturnRef() {
Foo f;
return f;
}
int main() {
Foo &f = BrokenReturnRef();
cout << f.bar();
}
К сожалению, это сломано! Когда BrokenReturnRef
работает, f
находится в области видимости, и все круто. Затем вы возвращаетесь к main
и продолжаете ссылаться на f
. Фрейм стека, который создал f
, исчез, и это местоположение больше недействительно, и вы ссылаетесь на ненужную память. В этом случае у вас будет для возврата по значению (или для выделения нового указателя в куче).
Единственное исключение из правила "не возвращать ссылки" - это когда вы знаете, что память превзойдет стек. Вот как STL реализует operator[]
для своих контейнеров.
Надеюсь, это поможет! :)