본문 바로가기

공부/c++

레퍼런스

반응형
참조 : http://lejewk.tistory.com/82

레퍼런스
는 c++에서 새로 도입된 개념이다. 레퍼런스란 변수명을 갖고있는 메모리주소에 또하나의 변수명을 지어주는것과 같다. 그렇다면 변수란 무엇일까? 그냥 변하는 값, 또는 상수,변수를 대입할수있는 이름 정도로 생각하는것은 안된다. 물론 그런 의미에서 변수일지몰라도 레퍼런스를 이해하려면 한가지 개념을 더 숙지해야만한다.
변수란 "메모리주소에 접근하기위해 사용자가 직접 정해준 이름" 라고 한가지 더 정의하겟다.

레퍼런스란말은 c언어에서 "Call-by-value" 를 배울때 가치 텨나온 개념중 "Call-by-Reference " 에서 레퍼런스의 말과 같다. 물론 완벽하게 같진 않지만 하는 행위는 비슷하다.

Call-by-value : 함수 에서 함수로 값을 넘겨줄때 값을 복사하여 넘겨주는 방식
Call-by-Reference : 함수 에서 함수로 값을 넘겨줄때 값이 아닌 주소를 복사하여 넘겨주는 방식

위에서 레퍼런스에 관해 많이도 언급햇는데 그럼 어떻게 사용하느냐?
&를 이용하여 레퍼런스를 선언하면된다. 시작부터 &기호를 통해 선언이라고 말하면 좀 이해가 안될수도 있어서 포인터와 견주어 설명해볼까함.

int * pt; : int형 pt를 선언하되 앞에 * 연산자를 붙임으로써 int형 포인터 변수를 선언한것과 같다.
그렇다면 &를 어디에 사용하는지도 짐작이 갈것이다.

int & val; : int형 레퍼런스 val을 선언한것이다.
그럼 어디에 쓰느냐? 바로 메모리주소에 이름을 달아줄때 쓴다. 그럼 여기서 바로 의문점이 생긴다. 어디에 있는 메모리주소에 이름을 달아줄것인가..?;; 답은 미리 선언된 변수명을 갖고있는 메모리주소에 해주는것이다. 즉, 레퍼런스를 선언하기전에 무조건 변수를 선언해야만 한다.

레퍼런스의 제약
-선언과 즉시 초기화 해줘야만한다.
-상수로 초기화 할수없다. 이유는 즉, 상수는 메모리주소를 갖고있지 않기때문이다. 변수를 int 10으로 만드는넘은없다 -ㅅ-;



간단한 레퍼런스의 예제

#include<iostream>

using std::cout;
using std::endl;

int main(){
    int val=0;
    int &ref = val;    //레퍼런스 초기화

    val++;
    ref++;

    cout<<val<<endl
        <<ref<<endl;
    return 0;
}

사용자 삽입 이미지







코드 해석 : int형의 크기를 가진 메모리를 할당후 val라는 이름을 붙여주었다. 그리고 그 메모리에 다시 ref라는 이름을 붙여준것이다. val과 ref는 같은 메모리를 가르키고 있다. 같은 메모리주소를 가르키고 있기때문에 val가 값을 증가시키던 ref가 값을 증가시키던 출력되는 값은 항상 같다.

그렇다면 레퍼런스와 변수명은 차이가 없는걸까? 딱짤라서 없다 ㅋㅋㅋ;; 만드는 과정에서 차이만 날뿐 그후엔 100% 동일한 변수명으로 움직인다.

레퍼런스는 Call-by-Reference를 구현함에 있어서 꽤나 쉽게 구현이 가능하다.

#include<iostream>

using std::cout;
using std::endl;

void swap(int &,int &);

int main(){
    int a=10, b=20;

    cout<<"swap 전"<<endl
        <<"a = "<<a<<endl
        <<"b = "<<b<<endl<<endl;
   
    swap(a,b);
   
    cout<<"swap 후"<<endl
        <<"a = "<<a<<endl
        <<"b = "<<b<<endl;

    return 0;
}

void swap(int & x , int & y){
    int temp = x;
    x = y;
    y = temp;
}

사용자 삽입 이미지












일단 포인터를 사용하지않고도 동일한 메모리주소를 가질수있다는점이 매우 유용하다. c언어에 포인터가 있어 사용한다고는 하지만 포인터를 많이 사용하는 프로그램이 꼭 좋은 프로그램일리는 없다. 또한 Call-by-value의 값복사 방식을 취하지 않고서도 값의 접근이 가능해서 매우 안전한거같다.

하지만 레퍼런스를 이용한 Call-by-Reference에도 단점은 있다. 바로 인자값의 오해이다. 함수 호출시 인자값을 넘겨줄때 &를 취하지않고 넘겼을경우 그것이 값을 복사하는 벨류방식인지 주소를 복사하는 레퍼런스방식인지 햇갈리기때문이다. 또하나의 단점은 리턴타입의 문제이다.
#include<iostream>

using std::cout;
using std::endl;

int & add(void);

int main(){
    int &ref = add();
    cout<<ref<<endl;
    return 0;
}

int & add(void){
    int val=10;
    return val;
}

스텍에 할당된 메모리는 블록구조에 의해
블록이 끝날때 메모리가 해제되는것쯤은 다 아는 사실이다. 그렇다면 add함수에서 할당된 val메모리는 메인함수까지가서 존재하는것일까? 확실히 저 코드를 컴파일해서 실행하면 정확한 값이 출력되긴 하지만 저런 예제와 비슷한 코드가 만일 수천줄의 다다르는 코드라면 절대로 원하는 값을 보장받기는 힘들것이다.

반응형