C++ передача указателя в функцию

195
25 сентября 2018, 11:40

Есть функция с сигнатурой:

void some_func(Object &object);

Как единственный параметр, она принимает ссылку. Создаем указатель:

Object *pObj = new Object();

Следующий шаг, логично напрашивается, что в верхнюю функцию мы должны передать ссылку, то есть:

some_func(&pObj);

Но компилятор этот вариант не принимает, а принимает такой:

some_func(*pObj);

Вопрос: Как я понял ранее, оператор *, это оператор разыменования и если мы передаем *pObj, тогда передаем содержимое ссылки в функцию some_func.

  • Почему вариант с указателями работает именно так?
  • Почему мы не можем в метод с таким параметром передать ссылку? (&rObj)
  • Что происходит при передаче some_func(*pObj)? По моим догадкам, создается копия объекта содержимого по ссылке указателя и потом передается как параметр в метод?
Answer 1

У вас 2 варианта. Либо использовать указатель, либо ссылку.

#include <iostream>
void some_func(int *obj){
    *obj = 6;
}
void some_func2(int &obj){
    obj = 5;
}
int main(){
    int obj;
    some_func(&obj);
    std::cout<<obj;//6
    some_func2(obj);
    std::cout<<obj;//5
    return 0;
}

&obj передаст указатель на объект, а не ссылку

Object *object = new Object();
//Создаётся указатель на новый объект
some_func(*object);
//* + (указатель на тип Object) = тип Object
//some_func принимает как аргумент объект, на который создаётся ссылка
//именно поэтому компилятор принимает такой вариант
Answer 2

Оператор &something - это оператор получения адреса something. Поэтому когда вы пишете &pObj, а pObj - указатель, вы передаете адрес указателя, а функция у вас принимает адрес какой-то переменной. Оператор * разыменовывания указателя дает вам доступ к памяти, на которую указатель указывает, можно сказать, что вы как-будто получаете какую то переменную. И затем, при передаче в вашу функцию, ее тип неявно преобразуется к ссылке.

bar(int& u);
int obj;
int* pointer = &obj;
bar(*pointer); // => bar(obj)
bar(obj); // то, что вы хотели
bar(pointer); // функция принимает аргумент типа int, а не int*. Ошибка.
bar(&pointer); /* передаст адрес указателя,
    это подойдет, если bar принимает указатель на указатель: bar(int** u) */
bar(&obj); /* передаст адрес obj,
    это подойдет, если bar принимает указатель: bar(int* u) */
Answer 3

Поскольку функция принимает ссылку на объект Object, вы должны передавать в эту функцию объект типа Object.

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

Если вы делаете так:

some_func(&pObj);

То, фактически, вы передаете адрес указателя на объект типа Object. То есть, вы передаете указатель на указатель на объект типа Object.

Выражение &pObj имеет тип Object **.

Answer 4
Токен &

В С++ этот токен имеет много значений:

  • Как бинарный оператор - оператор побитового "и".

    0b0011 & 0b0110; // 0b0010
    
  • Как унарный оператор - оператор взятия адреса у переменной.

    Type a;
    Type * ap = & a; // указатель на переменную типа Type
    
  • Как часть объвления типа - модификатора типа.

    Type & r; // ссылка на переменную типа Type
    
Токен *

Этот токен имеет такой же набор значений

  • Как бинарный оператор - оператор умножения.

    3 * 5; // 15
    
  • Как унарный оператор - оператор разыменовывания (взятие значения) переменной.

    Type a;
    Type & ar = * a; // ссылка на переменную типа Type
    
  • Как часть объвления типа - модификатора типа.

    Type * r; // указатель на переменную типа Type
    
Ссылки как значения

Работа с ссылками это как работа с их значениями. Нельзя переопределить ссылку чтобы она указывала на другой объект. Почти весь синтаксис для ссылки и для переменной значения одинаковый. Ссылки обяъвляются как Type & name. И в дальнейшем ведут себя как обычные пермененые содержащие значение.

struct Type {
   int a;
}
Type o;
Type & r = o;
o.a = 1; // ok
r.a = 2; // ok

Переменную-ссылку можно присвоить обеъкту и наоборот:

Type & ref = o;
Type val;
ref = val;
val = ref;

Передача как аргумент функции тоже явлется присваиванием так что предыдущее верно и для них:

void fun1(Type x) {}
fun1(o); // ok
fun1(r); // ok
void fun2(Type & y) {}
fun2(o); // ok
fun2(r); // ok
Почему нельзя присвоить указатель ссылке.

Одна из наиболее важных причин для этого заключается в том, что указатель может содержать адрес не валидного объекта. Этот процесс отдан программистам:

void funcp(Type* p) {
    if (p == nullptr) {
        Type & r = &p; // Здесь ОС пошлет сигнал и программа аварийно завершится
    }
    else {
        Type & r = &p; // Здесь все должно быть хорошо
    }
} 

Также указатель можно переназначить, но в случае передачи указателя в функцию это не имеет значения. Есть еще ряд других случаев когда ссылки и указатели служат совсем разным целям, но это повод для отедльной статьи.

READ ALSO
c++: std::in, _getch() работаю некорректно

c++: std::in, _getch() работаю некорректно

Для отладки программы необходимо приостанавливать работу программы, порционно выводить некоторую информацию на экран и затем по команде...

178
Помогите справиться с заданием [закрыт]

Помогите справиться с заданием [закрыт]

Создать программу, которая демонстрирует, что функция со своим собственным try-блоком не должна перехватывать все возможные исключения, выброшенные...

171
Компилятор g++ флаги, оптимизация

Компилятор g++ флаги, оптимизация

Изучаю работу с g++ интересует линковка, генерация кода, оптимизация, g++ содержит множество флагов оптимизаций помимо -O1

202