- 追加された行はこの色です。
- 削除された行はこの色です。
C++講座4回です
今回は参照渡しについてです。
今回のソースコード
&ref(reference.zip);
参照とは変数の別名です
別名なので単独で使うことはできません
別名なので元の変数とアドレスは同じになります
感覚的にはポインタに近いです
変数を参照渡しするにはポインタみたいに
int a = 5;
int& b = a; // bはaを参照(aの別名)
とします
注意としては参照は初期化しないと使えません(別名なので単体で使えません)
// ↓初期化してないのでコンパイルエラーとなる例
int& c;
上の例はあまりありがたみがないです
参照渡しの主な用途は関数の引数にポインタ代わりとして使います
ポインタと比べて間接参照演算子( * )を使わなくても、そのまま値にアクセスできます
ポインタ同様、関数の引数に変数を渡すときにデータのコピーが発生しないため、
構造体、クラスなどのデータサイズが大きい変数を参照渡しすると
メモリを占有しないし、処理速度も上がります
今回のサンプルです
ポインタとの違いを比較してください
// main.cpp
#include <iostream>
// ポインタを使って入れ替える例
void Swap(int* c,int* d){
printf("Swap関数ポインタver\t &c = %p c = %d,&d = %p d = %d\n",c,*c,d,*d);
int tmp = *d;
*d = *c;
*c = tmp;
}
// 参照渡しを使って入れ替える例
void Swap(int& c,int& d){
printf("Swap関数参照渡しver\t &c = %p c = %d,&d = %p d = %d\n",&c,c,&d,d);
int tmp = d;
d = c;
c = tmp;
}
int main()
{
int a = 5;
int& b = a; // bはaを参照(aの別名)
//int& c; // 参照は初期化しないと使えない(コンパイルエラー)
// アドレスの確認
printf("&a = %p, a = %d \n",&a,a);
printf("&b = %p, b = %d \n",&b,b);
b = 10;
// 同じアドレスのデータを書き換えている
printf("&a = %p, a = %d \n",&a,a);
printf("&b = %p, b = %d \n",&b,b);
printf("\n");
//--------------------------------------//
int c = 5,d = 8;
printf("メイン関数\t\t &c = %p c = %d,&d = %p d = %d\n",&c,c,&d,d);
printf("\n");
Swap(&c,&d);
printf("メイン関数\t\t &c = %p c = %d,&d = %p d = %d\n",&c,c,&d,d);
printf("\n");
Swap(c,d);
printf("メイン関数\t\t &c = %p c = %d,&d = %p d = %d\n",&c,c,&d,d);
return 0;
}
おまけに、参照渡しを使った3次元ベクトルクラスの足し算の例を挙げておきます
参照渡しはインスタンスの読み取り専用引数としてよく使われるのでマスターしておいて損はありません
// main.cpp
#include <iostream>
// 3次元ベクトル
class Vector3
{
public:
float x,y,z;
// ベクトルの足し算
Vector3& Add(const Vector3& vec);
};
Vector3& Vector3::Add(const Vector3& vec)
{
// 自分自身のインスタンスのメンバ変数にアクセスするにはthisポインタを使う
this->x += vec.x;
this->y += vec.y;
this->z += vec.z;
return *this; // 自分自身のインスタンスを返す
}
int main()
{
// 配列の初期化で構造体、クラスもメンバの初期化ができる
// 変数のデータ並び順(ここではx,y,zの順)にデータを初期化
Vector3 vec1 = {1.0f,2.0f,3.0f};
Vector3 vec2 = {3.0f,4.0f,5.0f};
Vector3 vec3;
printf("x = %.1f,y = %.1f,z = %.1f \n",vec1.x,vec1.y,vec1.z);
vec3 = vec1.Add(vec2);
printf("x = %.1f,y = %.1f,z = %.1f \n",vec3.x,vec3.y,vec3.z);
return 0;
}
thisポインタについて補足しておきます
thisはメンバ関数を呼び出したインスタンスのポインタに相当し、
メンバ関数内から自分のクラス内のメンバ変数やメンバ関数にアロー演算子を使ってアクセスできます
(実際にthis->と打ち込んで確認してみると良いでしょう)
thisは省略可能ですが、ここでの例のように
引数とメンバ変数名が重複した場合、メンバ変数を明示するのに使ったり
自分自身のインスタンスを戻り値に返す場合等に使います
ここではvec1からAddメンバ関数から呼び出されているため
thisはvec1のポインタとなります
関数の戻り値として参照を使う場合はポインタの戻り値と同様に少し注意が必要です
関数内のローカル変数を戻り値として返してはいけません
(関数を抜けると変数の寿命が尽きてしまうため)
ここではthisで自身のインスタンスを返していますが
他にもクラス内のメンバ変数や静的変数、動的なメモリ確保した変数など
関数を抜けても寿命が尽きない変数を返します
#vote(ok、次行こ、次[0],(;^ω^)?[0])