2009-12-25 4 views
5
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")); 
} ///:~ 

Учитывая приведенный выше пример, мы не смогли изменить переменную pass-by-reference, и мы не можем изменить переменную pass-by-value. Результат каждого метода одинаковый. Поскольку нет никакой разницы между этими двумя метод, почему C++ поддерживает оба метода?Любые различия между f (const string &) и f (const string)?

спасибо.

+2

Связанный http://stackoverflow.com/questions/1567138/const-t-arg-vs-t-arg/1567186#1567186 –

ответ

19

Существует разница между ними. Рассмотрим следующий пример:

#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 

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

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

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

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

1

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

8

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

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

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

+0

'почему C++ поддерживает оба метода', потому что сначала я не видел разница между этими двумя функциями. Я думал, что эти два одинаковы. После того, как вы сгенерировали сборку g ++ и msvc, это похоже на то же самое, передав один и тот же адрес константы. – Jichao

3

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

6

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

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

+1

'const string s' может понадобиться, если он лучше передает намерение – philsquared

+0

В чем разница в намерении между' string s' и 'const string s'? –

+0

Martin B: это внутренняя деталь для этой функции и может помочь кому-то написать эту функцию, но сигнатуры совместимы (вы можете сделать void f (int const); typedef void (* FP) (int); FP p = & f; '), и это не имеет никакого значения для вызывающих. – 2009-12-25 12:49:58

1

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

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

1

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

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

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

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

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

Смежные вопросы