LINUX.ORG.RU

суть ссылки, указателя и передачи по значению.

 , ,


0

2

Итак:

  • Указатель, указывает на участок памяти где располагается переменная, сам является переменной.
  • Ссылка -> ссылается на объект. (исправил)
  • Передача по значению - создается копия передаваемой переменной

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

Вопрос. В каких случаях применяется указатель, а в каких ссылки? Передача значением в этом всём вообще имеет место?

★★★★★

Последнее исправление: sniper21 (всего исправлений: 1)

Главное: указатель может быть равен нулю, а ссылка всегда ссылается на объект. Тогда по значению передается адрес переменной

dave ★★★★★
()

Ссылки придумали для неосиляторов указателей. Зачем? Потому что C++. Тебе еще многое предстоит узнать о C++. Именно узнать, не понять.

Имеем 2 вещи: указатель и копию. Дальше сам поймешь что где и зачем.

Deleted
()

Ссылка -> ссылается на имя объекта

Что за бред. Пока учишься, считай ссылку указателем, к которому можно обращаться через ., а не через ->

В каких случаях применяется указатель, а в каких ссылки?

Когда не хочется или нет возможности ставить & при передаче параметра или когда значение указателя не может быть nullptr..

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)

Указатели - это низкоуровневая абстракция, семантика памяти. Ссылки - высокоуровневая, семантика объектов.

Используй ссылки там, где достаточно семантики объектов, и не нужно спускаться на уровень памяти.

Но часто без указателей не обойтись: нулевой указатель, адресная арифметика, размещение объектов в куче.

Crocodoom ★★★★★
()

В каких случаях применяется указатель, а в каких ссылки?

Всегда где можешь, применяй ссылки. Под капотом они - то же самое что указатели, но по семантике удобнее и безопаснее (не могут быть неинициализированными и нулевыми). И если очень нужно использовать указатели, используй не сырые, а unique_ptr или shared_ptr.

Передача значением в этом всём вообще имеет место?

Имеет.

slovazap ★★★★★
()

Несколько случаев для использования указателей:

  • хранение их в контейнерах
  • если уже есть в тех же контейнерах, которые широко используются (иначе надоест разименовывать везде и будет смесь указателей/ссылок)
  • в полях классов, так как использование ссылок там делает невозможным присваивания и компилятор больше не генерирует оператор присваивания и конструктор копирования автоматически
xaizek ★★★★★
()
Ответ на: комментарий от xaizek

хранение их в контейнерах

можно, пожалуйста, «hello world» на c++ в качестве пояснения для контейнера std::vector<std::string>?

sniper21 ★★★★★
() автор топика
Ответ на: комментарий от slovazap

Имеет.

Кратко, но не понятно. :) Зачем вообще передавать значением если *var решение для всего. Ну например структура/класс огромная(ный), копию же её делать резона нет - много памяти.

sniper21 ★★★★★
() автор топика
Последнее исправление: sniper21 (всего исправлений: 1)
Ответ на: комментарий от sniper21

контейнерах, которые широко используются

Я имел в виду, широко используются в конкретном коде, а не в принципе. Например, если есть дерево с узлами:

struct Node {
    Node *parent;
    std::vector<Node *> children;
    // etc.
};
И функции для поиска/удаления узлов, которые принимают узлы. Тут использование ссылок по умолчанию, приведёт в постоянным разименованиям/взятиям адреса, которые просто создают визуальный шум.

xaizek ★★★★★
()
Ответ на: комментарий от sniper21

Нет, «решений для всего» в IT не бывает. Прежде всего, есть семантика. Дать функции доступ к объекту на чтение (const T&), дать функции доступ к объекту на чтение/запись (T&) и дать функции копию объекта (T) - это всё разные и валидные кейсы. Да, есть случаи когда нужно именно передать копию. Далее, с точки зрения производительности передать небольшие объекты по значению быстрее чем по ссылке. Наконец, теперь есть move semantics, которая позволяет передавать по значению бесплатно.

slovazap ★★★★★
()

Указатель, указывает на участок памяти где располагается переменная, сам является переменной.

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

Harald ★★★★★
()

Указатель указывает
Ссылка ссылается

А сепульки сепулируют?

Зачем вообще передавать значением если *var решение для всего.

Так себе решение. Лучше передавай параметры по ссылке. Если объект не подразумевает модификации - по константной.

Deleted
()
Ответ на: комментарий от fsb4000

пример по второй ссылке:

void f1(const string& s);  // OK: pass by reference to const; always cheap
void f2(string s);         // bad: potentially expensive
void f3(int x);            // OK: Unbeatable
void f4(const int& x);     // bad: overhead on access in f4()

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

почему четвертое - bad? Всмысле откуда overhead - при передаче строки в первом пункте все нормально же.

sniper21 ★★★★★
() автор топика
Последнее исправление: sniper21 (всего исправлений: 2)
Ответ на: комментарий от sniper21

В четвертом случае для доступа к значению придется проделать дополнительную работу, так как аргумент — не само значение, а адрес, по которому это значение можно получить.

TeopeTuK ★★★★
()
Ответ на: комментарий от sniper21

Запилил мини бенч

#include <benchpress/benchpress.hpp>

 __attribute__((noinline)) int sum_ref(const int& a, const int& b)
{
    return a + b;
}


 __attribute__((noinline)) int sum_copy (const int a, const int b)
{
    return a + b;
}

BENCHMARK("sum_ref", [](benchpress::context* ctx) 
{
    int a = 5;
    int b = 9;
    ctx->reset_timer();
    for (size_t i = 0; i < ctx->num_iterations(); ++i) 
    {
        int res = sum_ref(a, b);
        benchpress::escape(&res);
    }
})

BENCHMARK("sum_copy", [](benchpress::context* ctx) 
{
    int a = 5;
    int b = 9;
    ctx->reset_timer();
    for (size_t i = 0; i < ctx->num_iterations(); ++i) 
    {
        int res = sum_copy(a, b);
        benchpress::escape(&res);
    }
})
Результаты (компилировал с -O3)
sum_ref  2 ns/op
sum_copy 0 ns/op
Понятно что бенч так себе, так как не может, посчитать время sum_copy, но видно что он быстрее...

__attribute__((noinline)) Использовался, что компилятор не заменил эту функцию, просто строчкой a + b.

Откуда оверхед? Вот можно посмотреть ассемблерный код: https://godbolt.org/g/DPggsU

При копировании мы просто суммируем переменные в регистрах. А при ссылке, в функцию передаются адреса переменных a и b, и чтобы использовать a и b, нужно перейти по адресам.

fsb4000 ★★★★★
()
Ответ на: комментарий от sniper21

Не за что. Вот ссылка:

https://github.com/cjgdev/benchpress

Может google benchmark или Nonius получше, но этот зато header only, и зависит только от stl, то есть легко переносить между системами и компиляторами, без заботы о линковке...

fsb4000 ★★★★★
()
Ответ на: комментарий от deep-purple

Это не грабля, так делать нельзя и незачем. Кроме того, константная функция и константная ссылка - это примерно как карма и карман.

Deleted
()

Люди, ну хватит уже обманывать народ. Вполне может ссылка быть nullptr. Любой указатель может быть превращён в ссылку и наоборот, причём это даже не считается за хак.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Это считается за UB, так что не может.

anonymous
()

Указатель, указывает на участок памяти где располагается переменная

Если уж нужна точность, то располагается там объект а не переменная.

anonymous
()
Ответ на: комментарий от Deleted

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

А исключения придумали для неосиляторов err != nil.

oldstable
()

Решил сделать бенчмарк ещё и для относительно большой структуры.

#include <benchpress/benchpress.hpp>

struct big
{
double first;
double second;
double third;
double fourth;
double fifth;
};

 __attribute__((noinline)) double sum_ref(const big& a, const big& b)
{
    return a.first + b.second;
}


 __attribute__((noinline)) double sum_copy (const big a, const big b)
{
    return a.first + b.second;
}

BENCHMARK("sum_ref", [](benchpress::context* ctx) 
{
    big a;
    a.first = 9.0;
    big b;
    b.second = 5.0;
    ctx->reset_timer();
    for (size_t i = 0; i < ctx->num_iterations(); ++i) 
    {
        double res = sum_ref(a, b);
        benchpress::escape(&res);
    }
})

BENCHMARK("sum_copy", [](benchpress::context* ctx) 
{
    big a;
    a.first = 9.0;
    big b;
    b.second = 5.0;
    ctx->reset_timer();
    for (size_t i = 0; i < ctx->num_iterations(); ++i) 
    {
        double res = sum_copy(a, b);
        benchpress::escape(&res);
    }
})
Результаты
sum_ref   2 ns/op
sum_copy  12 ns/op
Если в структуре big, только 2 double, то
sum_ref   2 ns/op
sum_copy  2 ns/op
Если в структуре big, 3 или 4 double, то sum_copy, тоже 12 ns. Неожиданный результат немного, тем что время sum_copy с 3,4,5 double одинаковое...

fsb4000 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.