Есть ли различия между f (const string &) и f (const string)?

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 ++ поддерживаете оба метода?

благодаря.


person Jichao    schedule 25.12.2009    source источник
comment
Связанный stackoverflow.com/questions / 1567138 / const-t-arg-vs-t-arg /   -  person Alexey Malistov    schedule 25.12.2009


Ответы (7)


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

#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.

person Steve Jessop    schedule 25.12.2009

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

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

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

person Gregory Pakosz    schedule 25.12.2009
comment
why do c++ support both methods, потому что сначала я не видел разницы между этими двумя функциями. Я подумал, что это одно и то же. После того, как я покопался в сгенерированной сборке g ++ и msvc, это похоже на то же самое, передав один и тот же адрес константы . - person Jichao; 16.01.2010

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

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

person Salv0    schedule 25.12.2009
comment
const string s может понадобиться, если он лучше передает намерение - person philsquared; 25.12.2009
comment
В чем разница в намерениях между string s и const string s? - person Martin B; 25.12.2009
comment
Мартин Б: это внутренняя деталь функции и может помочь тому, кто должен написать эту функцию, но подписи совместимы (вы можете сделать void f(int const); typedef void (*FP)(int); FP p = &f;), и это не имеет значения для вызывающих. - person ; 25.12.2009

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

person josefx    schedule 25.12.2009

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

person shoosh    schedule 25.12.2009

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

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

person philsquared    schedule 25.12.2009

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

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

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

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

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

person Johannes Schaub - litb    schedule 25.12.2009