Объекты С++: когда следует использовать указатель или ссылку
Я могу использовать объект как указатель на него или его ссылку. Я понимаю, что разница заключается в том, что указатели должны быть удалены вручную, а ссылки остаются до тех пор, пока они не выйдут из области видимости.
Когда я должен использовать каждый из них? Какова практическая разница?
Ни один из этих вопросов не ответил на мои сомнения:
Ответы
Ответ 1
Ссылка - это в основном указатель с ограничениями (должен быть привязан к созданию, не может быть отскоком/нулем). Если для вашего кода имеет смысл использовать эти ограничения, то использование ссылки вместо указателя позволяет компилятору предупредить вас о том, что они случайно нарушают их.
Он очень похож на квалификатор const
: язык может существовать без него, он просто присутствует как бонусная функция, которая упрощает разработку безопасного кода.
Ответ 2
"Я должен удалить и указать, что они остаются, пока их область не закончится."
Нет, это совершенно неправильно.
Объекты, выделенные с помощью new
, должны быть удалены [*]. Объекты, не выделенные с помощью new
, не должны удаляться. Возможно иметь указатель на объект, который не был выделен с помощью new
, и возможно иметь ссылку на объект, который был выделен с помощью new
.
Указатель или ссылка является способом доступа к объекту, но не является самим объектом и не имеет никакого отношения к тому, как был создан объект. Концептуальная разница заключается в том, что ссылка - это имя для объекта, а указатель - это объект, содержащий адрес другого объекта. Практические различия, как вы выбираете, какой из них использовать, включают в себя синтаксис каждого и тот факт, что ссылки не могут быть пустыми и не могут быть повторно установлены.
[*] с delete
. Массив, выделенный с помощью new[]
, должен быть удален с помощью delete[]
. Существуют доступные инструменты, которые могут помочь отслеживать выделенные ресурсы и делать эти вызовы для вас, называемые интеллектуальными указателями, поэтому довольно редко нужно явно делать вызов самостоятельно, а не просто организовывать его, но тем не менее он должно быть сделано.
Ответ 3
У вас много ситуаций, когда параметр не существует или является недопустимым, и это может зависеть от семантики кода выполнения. В таких ситуациях вы можете использовать указатель и установить его в NULL (0), чтобы сигнализировать об этом состоянии. Кроме того,
- Указатель может быть переназначен новым
государство. Ссылка не может. Это
желательно в некоторых ситуациях.
- Указатель помогает передать семантику владельца. Это особенно полезно
в многопоточной среде, если состояние параметра используется для выполнения в
отдельный поток, и вы обычно не проводите опрос до выхода потока. Теперь поток может удалить его.
Ответ 4
suszterpatt уже дал хорошее объяснение. Если вы хотите, чтобы эмпирическое правило было легко запомнить, я бы предложил следующее:
Если возможно использовать ссылки, используйте указатели, только если вы не можете их избежать.
Еще короче: предпочитайте ссылки по указателям.
Ответ 5
Вот еще один ответ (возможно, я должен был отредактировать первый, но, поскольку он имеет другой фокус, я думал, что было бы хорошо, если бы они были отдельными).
Когда вы создаете указатель с new
, память для него зарезервирована и сохраняется до тех пор, пока вы не назовете delete
на нем, но срок службы идентификатора по-прежнему ограничивается концом блока кода. Если вы создаете объекты в функции и добавляете их во внешний список, объекты могут оставаться в памяти в памяти после возвращения функции, и вы все равно можете ссылаться на них без идентификатора.
Здесь (упрощенный) пример из Umbra, рамки С++, которую я разрабатываю. Там содержится список модулей (указателей на объекты), хранящихся в движке. Механизм может добавить объект к этому списку:
void UmbraEngine::addModule (UmbraModule * module) {
modules.push(module);
module->id = modules.size() - 1;
}
Получить одно:
UmbraModule * UmbraEngine::getModule (int id) {
for (UmbraModule **it=modules.begin(); it != modules.end(); it++) {
if ((*it)->id == id) return *it;
}
}
Теперь я могу добавлять и получать модули, не зная их идентификаторов:
int main() {
UmbraEngine e;
for (int i = 0; i < 10; i++) {
e.addModule(new UmbraModule());
}
UmbraModule * m = e.getModule(5); //OK
cout << m << endl; //"0x127f10" or whatever
for (int j = 0; k < 10; j++) {
UmbraModule mm; //not a pointer
e.addModule(&mm);
}
m = e.getModule(15);
cout << m << endl; //{null}
}
Список модулей сохраняется на протяжении всей продолжительности программы, мне не нужно заботиться о продолжительности жизни модулей, если они создаются с помощью new
:). Так что в основном это - с указателями, вы можете иметь долгоживущие объекты, которым когда-либо не нужен идентификатор (или имя, если хотите), чтобы ссылаться на них:).
Еще один приятный, но очень простой пример:
void getVal (int * a) {
*a = 10;
}
int main() {
int b;
getVal(&b);
return b;
}
Ответ 6
Эмм... не совсем. Это IDENTIFIER, который имеет область действия. Когда вы создаете объект с помощью new
, но его область видимости заканчивается, вы можете потерять память (или нет - зависит от того, чего вы хотите достичь) - объект находится в памяти, но у вас нет средств ссылаясь на него больше.
Разница в том, что указатель - это адрес в памяти, поэтому, если у вас есть, скажем, этот код:
int * a = new int;
a
- указатель. Вы можете распечатать его - и вы получите что-то вроде "0x0023F1" - это просто это: адрес. Он не имеет значения (хотя некоторое значение сохраняется в памяти по этому адресу).
int b = 10;
b
- это переменная со значением 10. Если вы ее распечатаете, вы получите 10
.
Теперь, если вы хотите, чтобы a
указывал на адрес b
, вы можете сделать:
a = &b; //a points to b address
или если вы хотите, чтобы адрес, указанный a
, имел значение b
:
*a = b; //value of b is assigned to the address pointed by a
Пожалуйста, скомпилируйте этот образец и прокомментируйте/раскомментируйте строки 13 и 14, чтобы увидеть разницу (обратите внимание, где указаны идентификаторы и WHAT VALUE). Я надеюсь, что результат будет понятным.
#include <iostream>
using namespace std;
int main()
{
int * a = new int;
int b = 10;
cout << "address of a: " << a << endl;
cout << "address of b: " << &b << endl;
cout << "value of a: " << *a << endl;
cout << "value of b: " << b << endl;
a = &b; //comment/uncomment
//*a = b; //comment/uncomment
cout << "address of a: " << a << endl;
cout << "address of b: " << &b << endl;
cout << "value of a: " << *a << endl;
cout << "value of b: " << b << endl;
}
Ответ 7
Сначала ответьте на последний вопрос. Тогда первый вопрос будет иметь больше смысла.
Q: "В чем практическая разница [между указателем и ссылкой]?"
A: Ссылка - это только локальный псевдоним для другой переменной. Если вы передадите параметр по ссылке, то этот параметр будет точно такой же переменной, как тот, который был указан в вызывающем операторе. Однако внутри обычно нет разницы между указателем и ссылкой. Ссылки предоставляют "синтаксический сахар", позволяя вам уменьшить количество ввода, которое вы должны делать, когда все, что вам действительно нужно, - это доступ к одному экземпляру заданной переменной.
Q: "Когда я должен использовать каждый из них?"
A: Это будет вопрос личных предпочтений. Вот основное правило, за которым я следую. Если мне понадобится манипулировать переменной в другой области видимости, и эта переменная является либо внутренним типом, либо классом, который должен использоваться как собственный тип (т.е. std::string и т.д.)), Либо класс const экземпляр, затем я передаю по ссылке. В противном случае я пройду по указателю.
Ответ 8
Дело в том, что вы не можете восстановить ссылку на другой объект. Ссылки связаны временем компиляции и не могут быть нулевыми или отскоками. Поэтому указатели не являются излишними, если вы сомневаетесь в том, что:)
Ответ 9
В качестве моего учителя С++, указатели указывают на расположение памяти, а ссылки - псевдонимы. Следовательно, основное преимущество заключается в том, что они могут использоваться так же, как и имя объекта, к которому они относятся, но в области, где объект недоступен, передавая его там.
В то время как указатели могут быть перенаправлены в другое место, ссылки как постоянные указатели не могут быть перенаправлены. Поэтому ссылки can not могут использоваться для перемещения массивов в функциях и т.д.
Однако указатель, являющийся отдельным объектом, занимает некоторую память, но ссылка, такая же, как упомянутый объект, не занимает дополнительного пространства. Это одно из его преимуществ.
Я также прочитал, что время обработки ссылок меньше,
как
int и я = b;
i ++; занимает меньше времени, чем
int * j = b;
(* j) ++;
но я еще должен подтвердить это. Если кто-то может пролить свет на это утверждение, было бы здорово.
Комментарии приветствуются:)