Подтвердить что ты не робот

Почему я должен использовать ссылочные переменные вообще?

В качестве моего первого языка программирования я изучил Java, но так как я перешел в другой университет, теперь изучаю С++.

Исходя из Java и изучая основы С++, я читал о ссылках и ссылочных переменных. И насколько они опасны, и как быть осторожными с ними и так далее.

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

Как-то это стоит того или просто реликвия с того времени, когда оперативная память была размером около 64 МБ?

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

4b9b3361

Ответ 1

Проблема не связана с самой ссылкой.

Проблема в том, что в С++ время жизни объектов управляется по-разному, чем в Java или другой среде выполнения с сборщиком мусора. У С++ нет стандартного встроенного сборщика мусора. Время жизни объектов С++ может быть автоматическим (в пределах локальной или глобальной области) или вручную (явно выделено/освобождено в кучу).

Ссылка на С++ - просто простой псевдоним для объекта. Он ничего не знает об объекте жизни ради эффективности. Программист должен заботиться об этом. Исключение является особым случаем, когда ссылка привязана к временному объекту, в этом случае время жизни временного объекта распространяется на время жизни связанной ссылки. Подробности здесь.

Ссылка является важной частью базовых концепций С++, и вы просто не можете избежать ее использования для 90% задач. В противном случае вы должны использовать указатели, которые обычно хуже: -)

Например, когда вам нужно передать объект в качестве аргумента функции по ссылке вместо по значению, вы должны использовать ссылки:

void f(A copyOfObj);       // Passed by value, f receives copy of passed A instance
void f(A& refToObj);       // Passed by ref, f receives passed A instance itself, modifiable
void f(const A& refToObj); // Passed by const ref, f receives passed A instance itself, non modifiable

Ответ 2

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

Ответ 3

Ссылка - это указатель с ограничениями. Ссылка является способом доступа к объекту, но не является самим объектом. Если для вашего кода имеет смысл использовать эти ограничения, то использование ссылки вместо указателя позволяет компилятору предупредить вас о том, что они случайно нарушают их.

Ответ 4

Как вы только что пришли из Java, я могу себе представить, что у вас проблемы с множеством опасных вещей, которые C/С++ позволяет вам делать. На мой взгляд, разница здесь:

  • C/С++ позволяет вам делать много мощных и опасных вещей. Вы можете делать много замечательных вещей, но также стрелять в ногу.
  • Java ограничивает то, что вы можете сделать во имя безопасности

Что вам не нравится ( "указатели/ссылки опасны" ) - это именно то, что мне нравится ( "это позволяет мне делать много вещей, а если испортить его по моей вине" ) о С++. Это, по-моему, вопрос программирования языка.

Были сделаны ссылки, чтобы избежать некоторых опасностей указателей, но, с другой стороны, даже указатели по-прежнему используют помимо очевидного быстрого доступа. Например, скажем, что у вас есть низкоуровневый объект, который выполняет некоторую полезную работу и делится между несколькими объектами высокого уровня. Он может использоваться по разным причинам (например, он обновляет внутреннее состояние каждый раз, когда он делает полезную работу, поэтому копирование не является вариантом).

class HL{
    private:
    LL &object;

    public:
    HL(LL &obj){
        object = obj; // initialization of reference
    }
    // a no-argument constructor is not possible because you HAVE to
    // initialize the reference at object-creation (or you could initialize
    // it to a dummy static object of type LL, but that a mess)
    void some_routine(){
        ....
        object.useful_work();
        ...
    }
}

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

class HL{
    private:
    LL *object;

    public:
    HL(LL &obj){
        object = &obj; // initialization of reference
    }
    HL(){} // no-argument constructor possible
    void some_routine(){
        ....
        if (object != NULL)
           object->useful_work();
        ...
    }
}

Кроме того, что касается использования вызова по ссылке, это наиболее полезно, когда вы передаете большую структуру функции, например. vector<int>.

void function(vector <int> a); // call by value, vector copied every time
void function(vector <int> &a); // call by reference, you can modify
                                // the vector in the function
void function(const vector <int> &a); // if you don't want to be able to modify
                                      // it, you can add const for "protection"

Ответ 5

Фактический вопрос: "зачем использовать ссылки вместо указателей".

Я еще не понял этого. В настоящее время я использую ссылки

  • потому что вы не можете назначить им. В худшем случае это ничего не значит для компилятора, но есть вероятность, что компилятор сможет использовать эти знания.

  • часто, когда я хочу вернуть указатель, который всегда существует, или указать, что параметр необходим и может не быть nullptr (нет нулевой ссылки). Это не жесткое правило; иногда фактический указатель чувствует себя более естественным.

  • когда вещи просто запрашивают ссылки, такие как перегрузки операторов.

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

Ответ 6

Ссылки упрощают вызов по ссылке, поскольку вызывающему абоненту не нужно использовать оператор & при передаче объекта.

Вот так:

MyObject ob;

void foo_ref(MyObject& o){ ...}
void foo_ptr(Myobject* o){ ...}

//call

foo_ref(ob);
foo_ptr(&ob);

Кроме того, они позволяют инициализировать указатели в функции:

MyObject* ob = nullptr;
MySubject* sub = nullptr;

void foo_init(MyObject *& o, MySubject *& s) {

   o = new MyObject;
   s = new MySubject;
}

//call

foo_init(ob, sub);

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

MyObject* ob = nullptr;
MySubject* sub = nullptr;

void foo_init(MyObject ** o, MySubject ** s) {

   *o = new MyObject;
   *s = new MySubject;
}

//call

foo_init(&ob, &sub);

Ответ 7

Наиболее частое использование ссылок заключается в том, чтобы избежать дорогостоящей глубокой копии; по умолчанию С++ имеет семантику значений, и если вы пишете что-то вроде:

void f( std::vector<double> d );

Весь вектор будет копироваться каждый раз при вызове f. Итак, вы пишете что-то вроде:

void f( std::vector<double> const& d );

Одно место, где это особенно важно, - это определить конструктор копирования. Если вы должны были написать:

MyType( MyType other );

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

Вне конструкторов это действительно оптимизация и может быть считается преждевременной оптимизацией. Однако на практике почти универсальное соглашение - передавать типы классов по ссылке на const, а не по значению.

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

Ссылки могут также использоваться для предоставления "окна" в данные класса. Классическим примером является перегрузка operator[] в std::vector; он возвращает ссылку, чтобы вы могли изменять, принимать адрес и т.д. фактический элемент в массиве.

Помимо параметров и возвращаемых значений, в приведенных выше случаях, ссылки несколько редки. Если вы используете его как члена класса, для Например, вы должны знать, что вы не можете дать классу классический семантика присваивания. (Это не проблема, если класс не поддержка). Как глобальная переменная, они могут использоваться для скрытия реализация того, что упоминается, но это чрезвычайно важно редко. Как локальная переменная, о единственном использовании, которое я знаю, заключается в кэшировании результаты другого выражения, которое возвращает ссылку: например, если Я получаю someMap[aKey] большое количество раз в коротком блоке кода, имеет смысл написать:

ValueType& element = someMap[aKey];

в начале и используйте element после этого. (Обратите внимание, что это только делает потому что someMap[aKey] возвращает ссылку.)

Ответ 8

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