С++ reference и константные указатели в C/С++
Недавно, когда я объяснял основную разницу между указателями и ссылками (в контексте программирования на С++) кому-то, я рассказал обычные объяснения, которые говорят о различных соглашениях о передаче аргументов функции - Call on value, Call by pointer, Call by reference, и всю связанную с этим теорию о ссылках.
Но тогда я подумал, что бы ссылка С++ не имела в терминах передачи аргументов (Позволяет эффективно управлять передачей больших структур/объектов в памяти, в то же время сохраняет ее в безопасности, не позволяя вызываемому изменять любые переменные переданного объекта как ссылка, если наш дизайн требует этого)
Указатель const в C будет достигать того же самого, например. Если нужно передать указатель структуры, произнесите struct mystr * ptr, указав указатель структуры как константу -
func(int,int,(const struct mystr*)(ptr));
будет ли ptr не быть каким-то эквивалентом ссылки?
-
Не будет ли это работать так, чтобы эффективность памяти была не реплицирована (передается по указателю), но также безопасна, запрещая любые изменения в полях структуры из-за того, что она передается как const указатель.
В контексте объекта С++ мы можем передавать указатель объекта const вместо ссылки на объект, чтобы достичь такой же функциональности)
-
Если да, то какой сценарий использования в С++ нуждается в ссылках.
Любые конкретные преимущества ссылок и связанные с ними недостатки?
спасибо.
-AD
Ответы
Ответ 1
Существует два типичных сценария использования:
Во-первых: указатели обозначают необязательные аргументы. Поскольку ссылки не могут быть NULL
, но указатели могут, документировать в стиле кодирования, что любой аргумент, обозначенный как указатель, может быть NULL
, функция должна обрабатывать это. Необязательные аргументы могут быть const или non-const, а также обязательные (ссылочные) аргументы.
Второе: ссылки используются только в сочетании с ключевым словом const, поскольку синтаксис вызова предлагает семантику передачи по значению читателя, которая по определению является константой. Затем указатели используются только для аргументов, которые могут быть изменены вызываемым пользователем.
Я лично предпочитаю первый вариант, потому что каждый из четырех случаев "const reference", "non-const reference", "const pointer", "non-const pointer" имеет другое значение. Второй вариант различает только две "вещи": "функция может изменять это значение", а "функция не будет изменять это значение".
Ответ 2
Вы можете привязать константную ссылку к rvalue:
void foo(const std::string& s);
foo(std::string("hello"));
Но невозможно передать адрес rvalue:
void bar(const std::string* s);
bar(&std::string("hello")); // error: & operator requires lvalue
В язык были включены ссылки для поддержки перегрузки операторов. Вы хотите сказать a - b
, а не &a - &b
. (Обратите внимание, что последнее уже имеет значение: вычитание указателя.)
Ссылки в основном поддерживают передачу по ссылке, указатели в основном поддерживают ссылочную семантику. Да, я знаю, в С++ различие не всегда ясно.
Ответ 3
В терминах объекта-референта, какой код может манипулировать с учетом ссылки на const, это то же самое, что он может делать с указателем на объект const, а также то, что он может манипулировать с учетом неконстантной ссылки, такой же, как заданный указателем на неконстантный объект.
Какая ссылка препятствует выполнению вызываемой функции, это изменение объекта, на который ссылается ссылка, или арифметика указателя на ссылку.
В терминах вызывающего, ссылка const может быть привязана непосредственно к rvalue, и у вас есть хорошо определенная семантика при создании и уничтожении объектов, переданных в качестве аргументов. Это обычная идиома - для создания временного аргумента:
// declaration
void bar ( const Foo& );
// use
bar ( Foo() );
Но не сразу очевидно, что объект Foo в них имеет продолжительность жизни, которая превышает длину вызова функции:
// declaration
void bar ( const Foo* );
// use
Foo temp;
bar ( &temp );
// cast to avoid warning about taking address of temporary
bar ( &static_cast<const Foo&>( Foo() ) );
// helper function to same effect
template<typename T> const T* address_of ( const T& t) { return &t; }
bar ( address_of ( Foo() ) );
Хотя очевидно, что последнее, будучи просто вызовом функции, должно сделать это очевидным.
В основном, ссылки являются синтаксическим сахаром для указателей, которые указывают на одиночные объекты, а не на начало массивов.