9
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"));
} ///:~

上記の例を考慮すると、参照渡し変数を変更することも、値渡し変数を変更することもできませんでした。各メソッドの出力は同じです。これら2つのメソッドに違いがないため、C++を使用する理由両方の方法をサポートしますか?

ありがとう。

4

7 に答える 7

25

2つの間に違いがあります。次のことを考慮してください。

#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

参照が関数内でconstであるからといって、他の参照を介して参照を変更できないという意味ではありません(1つのオブジェクトに複数の参照またはポインターがある状況は「エイリアシング」と呼ばれます)。したがって、const参照を渡すこととconst値を渡すことには違いがあります。参照の場合、呼び出しが行われた後にオブジェクトが変更される可能性があります。値の場合、呼び出し先はプライベートコピーを持っていますが、これは変更されません。

それらは異なることをするので、C++ではあなたが望むものを選択することができます。

どちらの方法でもパフォーマンスに影響があります。値を渡す場合は、コピーを作成する必要があり、コストがかかります。しかし、コンパイラーは、関数だけがそのコピーへの参照を持つことができる可能性があることを認識します。これにより、他の最適化が可能になる可能性があります。ProcessStringByRefは、callback()が返されるまで、印刷用の文字列の内容をロードできません。ProcessStringByValueは、コンパイラーがそうする方が速いと考える場合、可能です。

通常、コピーははるかに高価であるため、命令の実行順序ではなく、コピーに関心があります。したがって、通常、重要なオブジェクトをコピーするには、可能な場合は参照を渡します。ただし、エイリアシングが実際には発生しない場合でも、特定の最適化を妨げることにより、エイリアシングの可能性がパフォーマンスに深刻な影響を与える場合があります。そのため、「厳密なエイリアシングルール」とrestrictC99のキーワードが存在します。

于 2009-12-25T13:06:13.180 に答える
9

f(const string&)const参照によって文字列を取得します:f参照によって渡された文字列オブジェクトを直接操作します:関連するコピーはありません。constただし、元のオブジェクトへの変更を防ぎます。

f(const string)文字列値を取ります。これfは、元の文字列のコピーが与えられることを意味します。をドロップしてもconst、値を渡すときに、文字列への変更はf戻ったときに失われます。

「C++が両方のメソッドをサポートするのはなぜですか?」とはどういう意味か正確にはわかりません。適用されるのは、一般的なオーバーロードルールです。

于 2009-12-25T11:40:23.217 に答える
7

f(string s)文字列sの値を渡します。つまり、コピーを作成し、渡した文字列の値で初期化します。コピーへの変更は、関数を呼び出すために渡した元の文字列には反映されません。f(const string s)とにかく元の値を変更できないため、constは冗長です。

代わりに、文字列はコピーされませんが、そのf(const string& s)文字列への参照を渡します。これは通常、大きなオブジェクトがある場合に実行されるため、「値渡し」によってオーバーヘッドが発生する可能性があります(これが、c ++が両方のメソッドをサポートする理由です)。参照渡しとは、渡す「大きな」オブジェクトの値を変更できることを意味しますが、const指定子のため、変更することはできません。それは一種の「保護」です。

于 2009-12-25T12:02:54.507 に答える
3

オブジェクトに変更可能なメンバーが含まれている可能性があります。これは、const参照を使用しても変更できます。

于 2009-12-25T11:54:35.413 に答える
1

outputStringByRef参照先の変数が、必要な限り存続し、変更されないようにするoutputStringByRef必要があります。渡した変数を使用するとoutputStringByVal、死ぬかスコープから外れる可能性があり、関数が持っているコピーはまだ問題ありません。

于 2009-12-25T11:39:26.503 に答える
1

関数内で文字列を変更できるという点では、(ほとんど)違いはありません。ただし、渡される内容には大きな違いがあります。オーバーconst mystring &ssロードは、文字列へのconst参照を取ります。変更することはできませんが、アドレス指定されているのと同じメモリです。文字列が長い場合、これは大きな要因になる可能性があります(文字列がコピーオンライトを使用して実装されていない場合)。フォームは文字列のconst mystring ssコピーを作成しているため、別のメモリがアドレス指定されます。

実際には、aが使用された場合、const mystring &ssフォームによって文字列変更される可能性がありますconst_cast<mystring&>が、ここではお勧めしません。

于 2009-12-25T12:04:14.597 に答える
1

コピーできないオブジェクトを印刷したいとします。

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

参照バージョンはスレッドをコピーしませんが、のアドレスを取得してthそのエイリアスを作成します。他のバージョンは、値を渡すときにスレッドをコピーしようとしますが、そのようなオブジェクトではコピーが意味をなさない場合があります(その場合、追加のスレッドが1つあるでしょうか?)。

オブジェクトのコピーをオブジェクトクローンとして考えると役立つ場合があります。上記のC++でのコピーthは、Javaの場合と同じオブジェクトへの別のハンドルを持つことを意味するだけではありません。つまり、指定されたオブジェクトを暗黙的に複製し、そのコピー全体を保持することを意味します。Object.cloneしたがって、質問は「Javaが参照のコピーと両方をサポートするのはなぜですか?」に似ています。-どちらも目的が異なります。

そして、結局のところ、パフォーマンスの問題もあります。リソースを大量に消費するオブジェクトのために何かを渡すたびに、コピーする必要はありません。

于 2009-12-25T13:05:13.243 に答える