С++ 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() ) );

Хотя очевидно, что последнее, будучи просто вызовом функции, должно сделать это очевидным.

В основном, ссылки являются синтаксическим сахаром для указателей, которые указывают на одиночные объекты, а не на начало массивов.