2014. 6. 17. 11:52

[c++] 레퍼런스, reference, int &ref = val; 레퍼런스 변수, 별명!


 레퍼런스 

-> 별명!

# 레퍼런스를 선언하는 방법 

int & ref = val;



#레퍼런스의 특징

"레퍼런스를 가지고 하는 연산은 레퍼런스가 참조하는 변수의 이름을 가지고 하는 연산과 같은 효과를 지닌다."


#include <iostream>


using std::cout;

using std::endl;



int main(void)

{

    int val = 10;

    int &ref = val;


    val++;

    cout << " ref : " << ref << endl;

    cout << " val : " << val << endl;


    ref++;

    cout << " ref : " << ref << endl;

    cout << " val : " << val << endl;


    return 0;

}

$ 2-3-1

 ref : 11

 val : 11

 ref : 12

 val : 12



# 레퍼런스에 대한 명확한 이해

void function(void)

{

   int val;     //1

   val = 20;

   ...

   int &ref=val; //2

}


변수란? 메모리 공간에 할당된 이름을 의미한다. 우리는 그 이름을 통해 메모리 공간에 접근하게 된다.

위의 코드는 그 과정과 같다.

1) 메모리 공간 할당 & 이름 부여

2) 이름만 하나더 부여


C 언어에서는 하나의 메모리 공간에 하나의 이름만을 부여할 수 있었다.

즉 C 언어에서는 하나의 메모리 공간에 둘 이상의 이름을 부여하지 못했다.

하지만 C++에서는, 레퍼런스를 선언하게 되면

"이름이 존재하는 메모리 공간에 하나의 이름을 더 부여"할 수 있다.

"레퍼런스 변수는 생성되는 방법에 있어서만 차이를 보일 뿐 만들어지고 나면 완전히 같은 것이다.!"





여기서 이 예제가 가능한 이유가 되겠다!

(레퍼런스를 이용한 call by reference 예제!)

#include <iostream>


using std::endl;

using std::cout;



void Swap(int &fnum1, int &fnum2)

{

    int temp;

    temp = fnum1;

    fnum1 = fnum2;

    fnum2 = temp;

}



int main(void)

{

    int num1 = 10;

    int num2 = 20;


    cout << "[Swap]함수 호출 전 " << endl;

    cout << "num1 : " << num1 << endl;

    cout << "num2 : " << num2 << endl;




    Swap(num1, num2);

    cout << "[Swap]함수 호출 후 " << endl;

    cout << "num1 : " << num1 << endl;

    cout << "num2 : " << num2 << endl;


    return 0;

}

$ 2-3-2

[Swap]함수 호출 전

num1 : 10

num2 : 20

[Swap]함수 호출 후

num1 : 20

num2 : 10




call by reference예제를 막연히 봤을 때는,

"어떻게 저렇게 하는게 가능하지? 

 &는 C에서는 주소로 접근하는 것이었는데, 어떻게 swap연산이 되는걸까?"

하는 막연한 의문이 있었다! 바로 레퍼런스 변수를 제대로 몰랐기 때문이다.!


여기서 예제와 같이 사용하면, 

Swap함수에서 변수로 선언한 fnum1, fnum2가 메인함수에서 선언한 num1, num2의 

별명이 되어! 그대로 swap이 가능한 것이다.!

즉 같은 메모리 공간에 이름만 하나 더 부여해서 사용하기에! 

(지역함수가 매개변수에 대해 메모리 공간을 따로 할당받지 않는 것으로 보인다.!)

fnum1은 num1의 별명으로,

fnum2는 num2의 별명으로 쓰였다!



이 상황을 자세히 알아보기 위해!

아얘 변수들의 주소를 출력하기로 마음 먹었다.!


#include <iostream>


using std::endl;

using std::cout;



void Swap(int &fnum1, int &fnum2)

{

    int temp;

    temp = fnum1;

    fnum1 = fnum2;

    fnum2 = temp;

    cout << "fnum1의 주소: " << &fnum1 << endl;

    cout << "fnum2의 주소: " << &fnum2 << endl;



}



int main(void)

{

    int num1 = 10;

    int num2 = 20;

    cout << "num1의 주소: " << &num1 << endl;

    cout << "num2의 주소: " << &num2 << endl;



    cout << "[Swap]함수 호출 전 " << endl;

    cout << "num1 : " << num1 << endl;

    cout << "num2 : " << num2 << endl;




    Swap(num1, num2);

    cout << "[Swap]함수 호출 후 " << endl;

    cout << "num1 : " << num1 << endl;

    cout << "num2 : " << num2 << endl;


    return 0;

}


$ 2-3-2

num1의 주소: 0x7fffeb761724

num2의 주소: 0x7fffeb761720

[Swap]함수 호출 전

num1 : 10

num2 : 20

fnum1의 주소: 0x7fffeb761724

fnum2의 주소: 0x7fffeb761720

[Swap]함수 호출 후

num1 : 20

num2 : 10





주소들이 정확히 같은 것으로 볼 수 있다.!

같은 주소공간을 별명으로 가르키기에!

메인함수의 num1, num2값이 변경되지 않았다.!

레퍼런스를 이용하면 더 명시적으로 쉽게 코딩이 가능한 것으로 보인다.!



물론 포인터 연산으로 구현하는 방법도 있지만

레퍼런스를 사용하면 메인함수의 변수의 값을 바꾸는게 더 편해 보인다.!


(단점) main함수만 봐서는 swap함수가 call-ByValue인지 Call-By-Rerence인지 알 수 없다.

프로그램이 길어진다면 확인하기 힘들다. 이 단점때문에 레퍼런스를 사용하지 않는 편이 낫다고 하는 전문가들이 많다고 한다. 흠.. 나는 잘 모르겠다. 레퍼런스를 총한 call by Rerence편한듯ㅎ




(참고) 포인터를 이용한 call by reference

포인터를 이용해서 포인터가 가리키는 메모리 공간에 직접 접근 가능

#include <iostream>


using std::endl;

using std::cout;



void Swap(int * fnum1, int * fnum2)

{

    int temp;

    temp = *fnum1;

    *fnum1 = *fnum2;

    *fnum2 = temp;

}



int main(void)

{

    int num1 = 10;

    int num2 = 20;


    cout << "[Swap]함수 호출 전 " << endl;

    cout << "num1 : " << num1 << endl;

    cout << "num2 : " << num2 << endl;




    Swap(&num1, &num2);

    cout << "[Swap]함수 호출 후 " << endl;

    cout << "num1 : " << num1 << endl;

    cout << "num2 : " << num2 << endl;


    return 0;

}

$ 2-3-2

[Swap]함수 호출 전

num1 : 10

num2 : 20

[Swap]함수 호출 후

num1 : 20

num2 : 10




# 레퍼런스의 제약

"레퍼런스는 선언과 동시에 초기화 되어야 한다." 

따라서 아래의 코드는 Error!


int &ref1 //초기화 되지 않았으므로 ERROR

int &ref2 = 10; //상수가 올 수 없으므로 ERROR





#레퍼런스를 리턴하는 함수의 정의

#include <iostream>


using std::cout;

using std::endl;


int& increment( int &val )

{

    val++;

    return val;

}


int main(void)

{

    int n = 10;

    int &ref = increment(n);


    cout << " n : " << n << endl;

    cout << "ref: " << ref << endl;

    return 0;

}


$ 2-6-1

 n : 11

ref: 11




/*

매개변수로 선언된 레퍼런스 val은 지역 변수와 마찬가지로 함수의 호출이 완료되면 사라질 뿐이다. 

그러나 이름만 사라질 뿐, val이라는 이름이 붙어있는 메로리 공간까지 사라지는 것은 아니다!

*/




# 레퍼런스를 리턴하는 잘못된 형태의 함수

"지역변수는 레퍼런스로 리턴 할 수 없다"!

#include <iostream>


using std::cout;

using std::endl;


int & function(void)

{

    int val = 10;

    return val;

}



int main(void)

{

    int &ref = function();

    cout << ref << endl;


    return 0;


}



$ gcc.sh 2-6-2.cpp

2-6-2.cpp: In function ‘int& function()’:

2-6-2.cpp:8: warning: reference to local variable ‘val’ returned





/*

함수가 종료되면 function함수의 지역변수 val은 사라져 버린다.

제대로 출력 되었다고 하더라도 이는 보장받을 수 없는 값이다.

** 지역변수를 레퍼런스로 리턴하는 일은 없어야 한다.

*/





출처 : 윤성우의 열혈강의 c++ 프로그래밍

       

Posted by Triany