web-dev-qa-db-ja.com

C ++で&と*で宣言された関数引数の違い

次の例を入力しました。

#include <iostream>
double f(double* x, double* y)
{
    std::cout << "val x: " << *x << "\n";
    std::cout << "val y: " << *y << "\n";
    return *x * *y;
}
double f2(double &x, double &y)
{
    std::cout << "val x: " << x << "\n";
    std::cout << "val y: " << y << "\n";
    return x * y;
}
int main()
{
    double a, b;
    a = 2;
    b = 3; 
    std::cout << f(&a, &b) << "\n";
    std::cout << f2(a, b) << "\n";
    return 0;
}   

関数fでは、*xを使用して値を取得できるポインターとしてxとyを宣言します。 fを呼び出すとき、渡された引数のアドレスを渡す必要があるため、&a, &bを渡します。 f2は、定義が異なることを除いて同じです。

さて、私の質問は次のとおりです。どちらもメモリ管理に関して本当に同じですか?両方とも、渡された値のコピーを作成せず、代わりに参照を渡しますか? f2xのアドレスを読み取ることができなかったので、f2について疑問に思います。そのため、[know x_yのf(住所と値がわかっています)。

前もって感謝します!

編集:わかりました。さらに調査を行ったところ、非常に有用なトピックが見つかりました。

ポインタと参照 Googleコーディングガイドラインへのリンクもあります http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Argumentsuseful(今理解しているように、それは主題の好みの形だと思います)もっとclear

54
tim

f2は、参照によって引数を取ります。これは、基本的に、渡す引数のエイリアスです。ポインターと参照の違いは、参照をNULLにすることはできないことです。 fを使用して、ポインターに渡すパラメーターのaddress(&演算子を使用)を渡す必要があります。参照渡しすると、パラメータを渡すだけでエイリアスが作成されます。

Const参照(const double& ref)は、関数内の引数を変更しない場合に優先され、引数を変更する場合は、非const参照を使用します。

NULLをパラメーターに渡す必要がある場合、ポインターは主に使用されます。明らかに、使用前にポインターがNULLでない場合は、関数内で確認する必要があります。

54
Tony The Lion

これは、引数を参照するたびに*を使用する必要がないようにするための単なる構文上の砂糖です。 &を使用して、f2xのアドレスを引き続き使用できます。

13
philfr

言及されていない別の違いは、参照が参照するものを変更できないことです。これは、元の質問に示されている関数呼び出しの例に大きな違いはありません。

int X(10), Y(20);
int *pX = X;
int& rY = Y;

*pX = 15; // change value of X
rY = 25;  // change value of Y

pX = Y;   // pX now points to Y

rYは常にYを指し、移動できません。

参照を使用して、ポインターのような単純な配列にインデックスを付けることはできません。

10
DanS

私の頭の中では、関数のパラメーターはalways値渡しです。 intを渡すのは簡単に想像できますが、doubleを渡すのはもっと大きく、structまたはclassを渡すのは非常に大きくなります。
しかし、何かにポインタを渡すことは、アドレスを渡すだけです値による(多くの場合、ポインタはintと同様にCPUにとって便利なサイズです。 )
参照は非常によく似ており、参照は確かにポインタと考えていますが、参照するオブジェクトが値によって渡されたように見えるようにするための構文糖衣を使用しています。

参照をconstポインターと考えることもできます。つまり、

_int i;
int j;
int* p = &i;           // pointer to i
int* const cp = p;     // cp points to i, but cp cannot be modified
p = &j;                // OK - p is modified to point to j
*cp = 0;               // OK - i is overwritten
cp = &j;               // ERROR - cp cannot be modified

int& ri = i;           // ri refers to i
ri = 1;                // i is overwritten
ri = j;                // i is overwritten again
                       // Did you think ri might refer to j?
_

したがって、ポインターは2倍の時間を行います。それはそれ自体が値ですが、逆参照するときに別の値を指すこともできます。例:_*p_。
また、参照パラメーターを持つことは、それを表現する方法がないため、関数の存続期間中に他のものを参照させることができないことを意味します。

参照はnullで初期化できないはずですが、これを考慮してください:

_void foo(int& i);

int* p = 0;
foo(*p);
_

これは、ポインタを使用する前にチェックする必要があることを意味しますが、参照はチェックできません。 foo()の実装は、アクセス違反を引き起こすiの読み取りまたは書き込みを試みる可能性があります。

上記の例では、ポインターpshouldは、fooの呼び出しで使用される前にチェックされています。

_if (p) foo(*p);
_
6
quamrana

両方の関数でxアドレスを読み取ることができたはずです。

_f2_でこれを行うには、もちろんxの前に_&_を付ける必要があります。なぜなら、xはdoubleへのreferenceであり、 アドレス

参照とポインタの違いに注目する価値は、前者をNULLにすることはできないということです。 mustを渡す必要があります(---)ポインターを提供するとき、NULLを渡すことを許可/定義するかどうかをドキュメントで指定する必要があります。

もう1つの違いは、読みやすさの問題です。ポインターの代わりに参照を使用すると(可能な場合)、_*_および_->_でコードが乱雑になりません。

2
ereOn