web-dev-qa-db-ja.com

C ++での参照によるオブジェクトの受け渡し

変数を参照で渡す C++(Cも同様)の通常の方法は次のとおりです。

void _someFunction(dataType *name){ // dataType e.g int,char,float etc.
/****
definition
*/
}

int main(){
    dataType v;
    _somefunction(&v);  //address of variable v being passed
    return 0;
}

しかし、驚いたことに、参照によってオブジェクトを渡すオブジェクト自体の名前が目的を果たしていることに気付きました(_&シンボルが必要)および関数の宣言/定義中に*記号は引数の前に必要です。次の例で明確になります。

// this
#include <iostream>
using namespace std;

class CDummy {
  public:
    int isitme (CDummy& param);     //why not (CDummy* param);
};

int CDummy::isitme (CDummy& param)
{
  if (&param == this) return true;
  else return false;
}

int main () {
  CDummy a;
  CDummy* b = &a;
  if ( b->isitme(a) )               //why not isitme(&a)
    cout << "yes, &a is b";
  return 0;
}

なぜこの特別な扱いがクラスで行われるのか理解するのに問題があります。ほとんどクラスに似ている構造でさえ、この方法では使用されません。 配列の場合のようにオブジェクト名はアドレスとして扱われますか?

26
KNU

あなたを混乱させているように見えるのは、参照渡しであると宣言されている関数(&)は実際のアドレスを使用して呼び出されません。つまり、&a

簡単な答えは、関数を参照渡しとして宣言することです。

void foo(int& x);

必要なのはこれだけです。その後、参照によって自動的に渡されます。

次のようにこの関数を呼び出します。

int y = 5;
foo(y);

yは参照渡しされます。

また、このようにすることもできます(しかし、なぜですか?マントラは:可能な場合は参照を使用し、必要な場合はポインターを使用します):

#include <iostream>
using namespace std;

class CDummy {
public:
    int isitme (CDummy* param);
};


int CDummy::isitme (CDummy* param)
{
    if (param == this) return true;
    else return false;
}

int main () {
    CDummy a;
    CDummy* b = &a;             // assigning address of a to b
    if ( b->isitme(&a) )        // Called with &a (address of a) instead of a
        cout << "yes, &a is b";
    return 0;
}

出力:

yes, &a is b
55
keyser

参照とは、実際にニースを味わうのに十分な砂糖のポインタです...;)

ただし、ポインターとは異なる構文を使用しているため、ポインターよりも参照を使用する方が少し簡単です。このため、ポインターを取る関数を呼び出すときに&は必要ありません-コンパイラーがそれを処理します。また、参照のコンテンツを取得するために*は必要ありません。

参照をエイリアスと呼ぶのはかなり正確な説明です-それは「同じものの別の名前」です。 aが参照として渡されると、aのコピーではなく、aを渡します。aのアドレスを渡すことで(内部的に)行われます。 、しかし、あなたはそれがどのように働くかを心配する必要はありません(あなた自身のコンパイラを書いている場合を除き、あなたはあなた自身のコンパイラを書くときに知っておく必要がある他の多くのことを心配する必要はありませんプログラミングしているとき]。

参照は、intまたはclassタイプに対しても同じように機能することに注意してください。

12
Mats Petersson

上記の場合の参照渡しは、実際のオブジェクトのaliasにすぎません。

実際のオブジェクトを別の名前で参照することになります。

pointer referencesと比較して、referencesが提供する多くの利点があります。

2
Uchia Itachi

さて、参照渡しと値渡しを混同しているようです。また、CとC++は異なる言語です。 Cは参照渡しをサポートしていません。

値渡しのC++の2つの例

// ex.1
int add(int a, int b)
{
    return a + b;
}

// ex.2
void add(int a, int b, int *result)
{
    *result = a + b;
}

void main()
{
    int result = 0;

    // ex.1
    result = add(2,2); // result will be 4 after call

    // ex.2
    add(2,3,&result); // result will be 5 after call
}

ex.1が呼び出されると、定数2および2は、スタック上にローカルコピーを作成して関数に渡されます。関数が戻ると、スタックはポップオフされ、スタック上の関数に渡されたものは事実上すべてなくなります。

ex.2でも同じことが起こりますが、今回はint変数へのポインターもスタックに渡されます。関数は、このポインター(単にメモリアドレス)を使用して、結果を「返す」ためにそのメモリアドレスの値を逆参照および変更します。関数はパラメーターとしてメモリアドレスを必要とするため、それにアドレスを指定する必要があります。これには、&変数resultの「address-of」演算子。

参照渡しのC++の2つの例

// ex.3
int add(int &a, int &b)
{
    return a+b;
}

// ex.4
void add(int &a, int &b, int &result)
{
    result = a + b;
}

void main()
{
    int result = 0;

    // ex.3
    result = add(2,2); // result = 2 after call
    // ex.4
    add(2,3,result); // result = 5 after call
}

これらの関数は両方とも最初の2つの例と同じ最終結果を持っていますが、違いはそれらの呼び出し方法とコンパイラーによる処理方法にあります。

最初に、参照渡しの仕組みを明確にします。参照渡しでは、通常、コンパイラー実装は、参照される変数にアクセスするために最終的な実行可能ファイルで「ポインター」変数を使用します(またはコンセンサスと思われます)が、これは必ずしも真である必要はありません。技術的には、コンパイラは単に参照変数のメモリアドレスを直接置き換えることができ、これは一般に考えられているよりも真実であると思われます。そのため、参照を使用すると、ほんのわずかであっても、実際にはより効率的な実行可能ファイルを生成できます。

次に、明らかに、参照渡しを使用する場合の関数の呼び出し方法は値渡しと変わりません。その効果は、関数内の元の変数に直接アクセスできることです。これは、実装の詳細を呼び出し元から隠すことによるカプセル化の結果です。欠点は、関数外の元の変数も変更しない限り、渡されたパラメーターを変更できないことです。大きなオブジェクトをコピーする必要がないことからパフォーマンスを改善したいが、元のオブジェクトを変更したくない関数では、参照パラメーターの前にconstを付けます。

最後に、ポインター変数とは異なり、作成後に参照を変更することはできません。また、作成時に参照を初期化する必要があります。

すべてをカバーし、それがすべて理解可能であったことを願っています。

1
SeanRamey

追加しなければならないことの1つは、Cに参照がないことです。

第二に、これは言語構文規則です。 &-アドレス演算子ですが、参照も意味します-すべて米国のケースに依存します

&の代わりに「参照」キーワードがあった場合、次のように書くことができます。

int CDummy::isitme (reference CDummy param)

しかし、これはC++であり、長所と短所を受け入れる必要があります...

0
LukeCodeBaker