Есть ли различия между f (const string &) и f (const string)? - PullRequest
6 голосов
/ 25 декабря 2009
class mystring {
 friend ostream& operator<<(ostream &out, const mystring ss) {
        out << ss.s;
        return out;
    }
private:
    string s;
public:
    mystring(const char ss[]) {
        cout << "constructing mystring : " << ss << endl;
        s = ss;
    }
};

void outputStringByRef(const mystring &ss) {
 cout << "outputString(const string& ) " << ss << endl;
}

void outputStringByVal(const mystring ss) {
 cout << "outputString(const string ) " << ss << endl;
}

int main(void) {
    outputStringByRef("string by reference");
    outputStringByVal("string by value");
    outputStringByRef(mystring("string by reference explict call mystring consructor"));
    outputStringByVal(mystring("string by value explict call mystring constructor"));
} ///:~

Рассматривая приведенный выше пример, мы не смогли изменить переменную передачи по ссылке, а также не смогли изменить переменную передачи по значению. Вывод каждого метода один и тот же. Поскольку между этими двумя методами нет разницы, почему C ++ поддерживает оба метода?

спасибо.

Ответы [ 7 ]

20 голосов
/ 25 декабря 2009

Между ними есть разница. Учтите следующее:

#include <iostream>
#include <string>
using std::string;

string g_value;

void callback() {
    g_value = "blue";
}

void ProcessStringByRef(const string &s) {
    callback();
    std::cout << s << "\n";
}

void ProcessStringByValue(const string s) {
    callback();
    std::cout << s << "\n";
}

int main() {
    g_value = "red";
    ProcessStringByValue(g_value);
    g_value = "red";
    ProcessStringByRef(g_value);
}

Выход:

red
blue

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

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

В любом случае для производительности есть последствия - когда вы передаете по значению, должна быть сделана копия, которая стоит. Но тогда компилятор знает, что только ваша функция может иметь какие-либо ссылки на эту копию, что может позволить другие оптимизации. ProcessStringByRef не может загрузить содержимое строки для печати, пока не вернется callback(). ProcessStringByValue может, если компилятор считает, что это происходит быстрее.

Обычно вы заботитесь о копии, а не о порядке выполнения инструкций, потому что обычно копия намного дороже. Поэтому обычно вы передаете ссылки, где это возможно, для объектов, которые нетривиально копировать. Но возможность наложения псевдонимов иногда имеет действительно серьезные последствия для производительности, поскольку предотвращает определенную оптимизацию, даже если на самом деле псевдонимов не происходит. Вот почему существуют "строгие правила наложения имен", а ключевое слово restrict в C99.

8 голосов
/ 25 декабря 2009

f(const string&) принимает строку по const ссылке: f работает непосредственно со строковым объектом, переданным по ссылке: копия не используется. const предотвращает изменение оригинального объекта.

f(const string) принимает строковое значение, что означает, что f предоставляется копия исходной строки. Даже если вы пропустите const, при передаче по значению любая модификация строки будет потеряна при возврате f.

Я не знаю точно, что вы подразумеваете под «почему C ++ поддерживает оба метода?». Это общие правила перегрузки.

6 голосов
/ 25 декабря 2009

f(string s) передать по значению строку s, другими словами, он создает копию и инициализирует ее значением передаваемой строки. Любые изменения в копии не будут распространяться на исходную строку, переданную вами для вызова функции. В f(const string s) const является избыточным, потому что вы все равно не можете изменить исходное значение.

В f(const string& s) вместо этого строка не копируется, но вы передаете ссылку на нее. Обычно это делается, когда у вас есть большой объект, поэтому «передача по значению» может привести к накладным расходам (поэтому c ++ поддерживает оба метода). Передача по ссылке означает, что вы можете изменить значение «большого» объекта, который вы передаете, но из-за спецификатора const вы не можете его изменить. Это своего рода «защита».

3 голосов
/ 25 декабря 2009

ваш объект может содержать mutable член, который может быть изменен даже с помощью константной ссылки

1 голос
/ 25 декабря 2009

Представьте, что вы хотите напечатать объект, который невозможно скопировать:

Thread th;
// ...
cout << th; // print out informations...

Эталонная версия не будет копировать поток, но примет адрес th и создаст для него псевдоним. Другая версия будет пытаться скопировать поток при передаче по значению, но копирование может не иметь смысла для такого объекта (будет ли тогда еще один дополнительный поток?).

Это может помочь вам подумать о копировании объекта как клонировании объекта . Копирование th выше в C ++ не означает просто иметь другой дескриптор к тому же объекту, что и в Java - это означает неявное клонирование обозначенного объекта и получение его полной копии. Таким образом, вопрос похож на «Почему Java поддерживает и Object.clone, и копирование ссылок?» - у обоих разные цели.

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

1 голос
/ 25 декабря 2009

Нет (почти) никакой разницы с точки зрения возможности изменения строки в функции. Однако есть большая разница с точки зрения того, что передается. Перегрузка const mystring &ss принимает константную ссылку на строку. Хотя это не может изменить это, это - та же самая адресуемая память. Если строка длинная, это может быть большим фактором (при условии, что строка не реализована с использованием copy-on-write ). Форма const mystring ss создает копию строки, поэтому будет использоваться другая память.

На самом деле форма const mystring &ss может изменить строку, если используется const_cast<mystring&> - хотя я бы не рекомендовал это здесь.

1 голос
/ 25 декабря 2009

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

...