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

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

Я новичок в С++. Недавно мы начали изучать ссылочные переменные в классе, и я очень смущен ими. Не обязательно, как их выполнять, поскольку я понимаю, что они переключают переменные значения, но больше по линиям ПОЧЕМУ разработчик хотел бы сделать такую ​​вещь? Что они достигают? Сохранят ли они память? Не избегают ли они возвращать информацию?

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

"Пользователь может пожелать получить оценку от одного до многих комнат. Ставки основаны на квадратных метрах от стен и/или потолка. По оценкам компании, для покрытия 200 SF настенного пространства требуется 2,5 часа 3,2 часа, чтобы нарисовать одну и ту же площадь на потолке. Уровень трудозатрат составляет 40 долларов США в час. Если работа по покраске WALLS составляет более 1400 SF пространства, тогда клиент получает 15% скидку на все квадратные метры выше 1400 квадратных футов. Скидка на окрасочные потолки не предоставляется.

Программа должна распечатать окончательный отчет о сметных расходах в профессиональном формате.

Программа должна спросить пользователя, хотят ли они делать больше вычислений до выхода."

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

И, по сути, studentID будет установлен в 21654. Я правильно понимаю это?

Попробуем еще раз:

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

Я должен использовать такие переменные, а также как (фактический синтаксис и размещение). Почти все здесь были велики, и я многому научился на этом предмете благодаря своим взаимодействиям с вами. Даже когда это повторяется и раздражает опытных кодировщиков, это для меня все ново, и мне нужно было участвовать в разговоре столько, сколько мне нужно. Например, я использовал Qaru для многих проектов, изучая, например, Java newString.equalsIgnoreCase(), и я восхищаюсь вашими знаниями. Я могу только сказать вам правду, если это не достаточно хорошо, это то, что есть.

Хорошо, позвольте мне рассмотреть мое понимание до сих пор:

Ссылочные переменные имеют тенденцию сокращать нежелательную модификацию переменных внутри функции и/или программы.

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

Это полезно, поскольку оно "перемещает" значения вокруг, сводя к минимуму копирование этих значений.

Ссылочные переменные изменяют существующие переменные в функциях/программах

Я не знаю, можете ли вы, ребята, прочитать это или нет, поскольку он был помечен как дубликат. Я играл с несколькими мини-программами, которые вы мне дали, перечитал часть моей книги, провел дальнейшие исследования и т.д., И я думаю, что я понимаю на начальном уровне. Эти ссылочные переменные позволяют вам изменять и/или использовать другие переменные в вашем коде, не перетаскивая их прямо в ваш код. Я не помню, какой пользователь использовал пример foo (hubble, bubble), но это был его/ее код, который, наконец, нажал кнопку. Вместо того, чтобы просто использовать значение, вы фактически используете и/или переназначаете переменную.

4b9b3361

Ответ 1

Здесь вы смешиваете две совершенно разные вещи. Три примера, чтобы показать, как две вещи работают, индивидуально, а затем вместе...

  • Функция может принимать параметр, переданный по значению, и возвращать значение.

    double foo (double y)
    {
        y = y + 200.0;
        return y;
    }
    
    void main(void)
    {
        double hubble = 50.0;
        double bubble = 100.0;
    
        hubble = foo(bubble);
    
        std::cout << "hubble=" << hubble << ", bubble=" << bubble << std::endl;
    }
    

    Обратите внимание, что поскольку это передается по значению, хотя foo() изменяет y, bubble не изменяется. hubble установлено значение, возвращаемое функцией foo().

    Затем вы получаете

    hubble=300, bubble=100
    
  • Функция может принимать параметр, переданный по ссылке, и изменять этот параметр.

    void foo (double& y)
    {
        y = y + 200.0;
    }
    
    void main(void)
    {
        double hubble = 50.0;
        double bubble = 100.0;
    
        foo(bubble);
    
        std::cout << "hubble=" << hubble << ", bubble=" << bubble << std::endl;
    }
    

    Затем вы получаете

    hubble=50, bubble=300
    

    Конечно, hubble не изменился. Но поскольку bubble передавался по ссылке, изменение в y внутри foo() меняет bubble, потому что это изменение происходит по фактической переменной, переданной, а не по скопированному значению.

    Обратите внимание, что здесь у вас нет инструкции "return". Функция ничего не возвращает - она ​​просто модифицирует переменную, которая передается ему.

  • И, конечно, вы можете использовать оба вместе.

    double foo (double& y)
    {
        y = y + 200.0;
        return y + 400.0;
    }
    
    void main(void)
    {
        double hubble = 50.0;
        double bubble = 100.0;
    
        hubble = foo(bubble);
    
        std::cout << "hubble=" << hubble << ", bubble=" << bubble << std::endl;
    }
    

    Затем вы получаете

    hubble=700, bubble=300
    

    Как и раньше, изменение y внутри foo() изменяет bubble. Но теперь функция также возвращает значение, которое устанавливает hubble.

Почему вы решили возвратить значение или изменить значение, переданное или сделать то же? Это полностью зависит от того, как вы пишете свой код.

Я согласен с вами в том, что здесь вам не нужно использовать ссылку "pass-by-reference". Я бы, наверное, просто вернул значение. Но это учебное упражнение, и вам сказали сделать это таким образом, так что вам нужно. Предположим, что ваша передача по ссылке - это скидка? Таким образом, функция "void discount (double & value)" принимает переданное значение и умножает его на 0,85. Это немного искусственно, но это продемонстрировало бы принцип.

Ответ 2

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

Согласно Bjarne Stroustrup FAQ:

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

void f1(const complex* x, const complex* y)    // without references
    {
        complex z = *x+*y;    // ugly
        // ...
    }

    void f2(const complex& x, const complex& y)    // with references
    {
        complex z = x+y;    // better
        // ...
    }

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

Ref<My_type> r :- new My_type;
r := 7;            // assign to object
r :- new My_type;    // assign to reference

В качестве альтернативы вы можете положиться на проверку типов (перегрузка). Для Пример:

Ref<My_type> r = new My_type;
r = 7;            // assign to object
r = new My_type;    // assign to reference

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

Ответ 3

Я приведу три причины, но их гораздо больше.

  • Избегайте ненужных копий.

    Предположим, что вы пишете такую ​​функцию:

    double price(std::vector<Room> rooms)
    {
           ...
    }
    

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

    В этом случае лучше использовать постоянную ссылку, которая предоставляет доступ к данным только для чтения:

    double price(const std::vector<Room>& rooms) { ... }
    
  • Использование полиморфизма

    Предположим, что теперь у вас есть разные типы комнат, возможно, CubicRoom и CylindricalRoom, которые наследуются от одного и того же базового класса Room.

    Невозможно написать:

    double price(Room room) { ... }
    

    а затем вызовите

    price(CylindricalRoom());
    //or
    price(CubicRoom());
    

    но вы можете, если вы определите price следующим образом:

    double price(Room& room);
    

    Все работает так же, как если бы вы прошли по значению.

  • Избегайте возвратов

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

    return price, fmtQuote
    

    Однако вы можете сделать:

    double price(Room room, std::vector<std::string>& quotes)
    {
        ...
        quotes.push_back(fmtQuote);
        return price
    }
    

    Очевидно, вы могли бы вернуть пару объектов std::pair<double, std::string>, но это означает, что вызывающий должен распаковать результат. Если вы намереваетесь часто вызывать указанную выше функцию, это быстро станет уродливым. В этом случае это связано с первой точкой: журнал всех кавычек будет расти, и вы не хотите копировать его для каждого вызова.

    Это типичный шаблон доступа для общих ресурсов: вы хотите, чтобы несколько функций/объектов получили дескриптор ресурса, а не копию этого ресурса.

Ответ 4

Ссылочные переменные являются более безопасной альтернативой указателям. Обычно, имея дело с указателями, вы действительно не заботитесь о указателе (ptr) так, как он указывает (*ptr); и все же программисты все время закручивают и управляют ptr вместо *ptr и так далее. Рассмотрим этот код:

void zeroize_by_pointer(int* p)
{
    p = 0; // this compiles, but doesn't do what you want
}

Сравните с эталонной версией,

void zeroize_by_reference(int& p)
{
    p = 0; // works fine
}

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

Ответ 5

Ссылки были представлены в основном для поддержки перегрузки операторов. Использование указателей для "передачи через ссылку" даст вам неприемлемый синтаксис в соответствии с Bjarne Stroustrup. Они также допускают сглаживание.

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

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

Ответ 6

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

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

Указатели, с другой стороны, могут иметь значение null. Если я пишу функцию, которая принимает указатель...

void increment(int* val)
{ 
    (*val)++;
}

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

Но напишите эту функцию со ссылкой, и цель понятна. Разрешено не разрешено.

Ответ 7

Вы почти всегда можете использовать ссылочные переменные (вместо того, чтобы переходить по значению): например...

// this function creates an estimate
// input parameter is the Rooms to be painted
// passed as a const reference because this function doesn't modify the rooms
// return value is the estimated monetary cost
Money createEstimate(const Rooms& rooms)
{
  ...
}

// this function adds paint to the rooms
// input parameter is the Rooms to be painted
// passed as a non-const reference because this function modifies the rooms
void paintRooms(Rooms& rooms)
{
  ...
}

Когда вы передаете по значению вместо передачи по ссылке, вы неявно создаете и передаете копию вещи...

// creates and passes a copy of the Rooms to the createEstimate function
Money createEstimate(Rooms rooms)
{
  ...
}

... который (создание копии) (часто, немного) медленнее, чем передача по ссылке (кроме того, создание копии может иметь побочные эффекты).

Как возможная небольшая оптимизация производительности и по соглашению (потому что люди не заботятся), она является общей для передачи по значению вместо pass-be-reference, когда тип мал и прост (он же "примитивный", тип), например:

// passes a copy of the x and y values
// returns the sum
int add(int x, int y)
{
  ...
}

... вместо...

// passes a reference to x and y
// returns the sum
int add(const int& x, const int& y)
{
  ...
}

См. также Передача изменяемого параметра в функцию С++, а также Почему есть параметры указателя?

Ответ 8

Существуют также различные типы ссылок. Мы имеем ссылки lvalue и rvalue, обозначенные соответственно & и &&. Как правило, ссылка указывает нам что-то о времени жизни объекта, который он ссылается, указатель этого не делает. Сравнить

void foo(int* i);
void foo(int& i);
void foo(int&& i);

В первом случае i может указывать на объект, который мы можем назначить, но что более важно, он также может быть nullptr или указывать на один конец прошлого массива. Таким образом, разыменование его может привести к поведению undefined. Проверка на nullptr достаточно проста, другая проверка - нет.

Второй случай и третий случай, i должны всегда ссылаться на действительный int, который мы также можем назначить.

Разница между ссылками rvalue и lvalue заключается в том, что ссылки rvalue/&& передают значение, которое ссылочное значение не требуется никому другому и как таковое, позволяет оптимизировать. Прочитайте std::move и переместите конструкторы, чтобы понять, что я имею в виду.


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

Ответ 9

Ссылочные аргументы больше используются при передаче объекта в качестве аргумента. Таким образом, вы не копируете всю переменную; обычно они поставляются с модификатором const, например:

void printDescription(const Person& person) { ... }

Таким образом вы не копируете объект.

Иногда тип возврата также задается как ссылка. Таким образом вы возвращаете один и тот же объект (а не его копию). Посмотрите на оператор << ostream. ostream& operator<< (streambuf* sb );.

С переменными вы можете подумать о том, где вы можете менять значения.

void swap(int & a, int & b) {
    int aux = a;
    int a = b;
    int b = aux;
}

Этот случай в Java, например, должен выполняться более сложным способом.

Ответ 10

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

Они не нужны из С++, они всего лишь синтаксический сахар вокруг них.

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

Я считаю, что программа на С++ лучше, если она полностью пропускает ссылочные переменные и использует только указатели.

Ваша функция в форме

double foo (double studentID* y)
{
  *y = 21654;
  return *y;
}

... будет делать то же самое, но на самом деле было бы лучше понятным.