Какие-либо различия между f (строка константы и) и f (строка константы)?

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

спасибо.

6
задан Jichao 25 December 2009 в 11:35
поделиться

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

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

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

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

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

.
21
ответ дан 8 December 2019 в 02:17
поделиться

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

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

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

.
8
ответ дан 8 December 2019 в 02:17
поделиться

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

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

6
ответ дан 8 December 2019 в 02:17
поделиться

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

.
3
ответ дан 8 December 2019 в 02:17
поделиться

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

1
ответ дан 8 December 2019 в 02:17
поделиться

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

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

.
1
ответ дан 8 December 2019 в 02:17
поделиться

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

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

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

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

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

1
ответ дан 8 December 2019 в 02:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: