레퍼런스란?

·2021년 2월 26일

c++ 언어

목록 보기
2/6

레퍼런스(참조자)??

: 별명을 뜻한다.

0. 언제 사용할까?

: 함수의 매개변수로 보낼때 포인터 대신 레퍼런스를 사용하면 메모리 생성 및 복사를 방지하고, 기존 할당된 메모리를 사용하는 것이므로 메모리를 할당을 줄일 수 있다.
call by address의 무방비한 메모리 접근을 방지한다!

1. 선언과 동시에 반드시 초기화를 해야한다.

  • 안하면 이와 같이 에러 발생.
int main() {
	int b = 2;

	int &c;


	return 0;
}

2. 한번 초기화가 이루어지면 다른 메모리로 변경 불가능하다.

=> 값 대입이 바뀐다. 메모리값은 변경 불능

#include <iostream>
using namespace std;


int main() {
	int b = 2;
	cout << "b의 주소 :"<< &b << endl;
	int &c = b;
	cout << "c의 주소 :"<< &c << endl;

	cout << "b의 값 : " << b << endl;
	cout << "c의 값 : " << c << endl;
	
	int d = 5;
	cout << "d의 주소 :" << &d << endl;
	c =d;

	cout << "메모리 주소값이 바뀌었을 까?" << endl;
	cout << "b의 주소 :" << &b << endl;
	cout << "c의 주소 :" << &c << endl;
	
	cout << "바뀌지 않았다. 이때는 값을 대입하는 동치이다" << endl;

	cout << c << endl;
	cout << b << endl;
	return 0;
}

3. 메모리값

  • 레퍼런스는 기존 변수의 다른 이름의 별명이므로 메모리를 따로 갖지는 않는다.

//내부에서 확인하기

#include <iostream>
using namespace std;


int main() {
	
	int a = 2;
	int *p_a = &a;

	cout << "a의 주소 : " << &a << ", a의 값 : " << a
		<< ", a의 size : " << sizeof(a) << endl;

	cout << endl;
	cout << "p_a의 주소 : " << &p_a << ", p_a의 값 : " << *p_a
		<< ", p_a의 size : " << sizeof(p_a) << endl;
	cout << endl;

	int &ref_a = a;

	cout << "ref_a의 주소 : " << &ref_a << ", ref_a의 값 : " << ref_a
		<< ", ref_a의 size : " << sizeof(ref_a) << endl;
	cout << endl;

	cout << "포인터를 메모리값을 따로 가지지만, 레퍼런스는 메모리값을 따로 갖지 않는다." << endl;
	return 0;
}
  • 비교는 다음 장에 정말 자세하게 서술하자
  • call by value
    : 매개변수가 원본의 복사본을 만들어서 사용된다.
    만약에 vector v가 크기 : 100 이 할당된 상태에서 매개변수로 사용된다면
    복사본이 100 * 4 크기 만큼의 메모리를 할당할 것이다.
    그리고 복사본에서 데이터를 바꾼다고 하더라도 원본에는 영향을 주지않는다.
    말 그대로 복사본이니까...
  • call by address 일 경우에
    : 매개변수가 4바이트 크기의 포인터 변수를 하나 만들고, 포인터 변수가 원본을 가리킴으로서 100개의 원본 값을 변경이 가능하다.
    그러나 call by address 의 경우 메모리에 접근이 가능하다는 문제가 있다.
#include <iostream>
using namespace std;

void change(int * p_a)
{
	*p_a = 5;
	cout << "p_a의 주소값은 ? " << &p_a << ", p_a의 값은 : " << p_a << 
		"p_a가 가리키고 있는 값은 : " << *p_a << endl;
	cout << "스코프 외부에서 call by address 사용할 경우 주소 만들어진다.  " << endl;
	cout << endl;
}


int main() {

	int a = 2;
	cout << "a의 주소값은 ? " << &a << ", a의 값은 : " << a << endl;
	cout << endl;

	change(&a);
	cout << "a의 주소값은 ? " << &a << ", a의 값은 : " << a << endl;
	cout << endl;

	return 0;
}

문제가 되는 점.

#include <iostream>
#include <algorithm>
#include <vector>
#include <time.h>
using namespace std;

void change(int * p_a)
{
	*p_a = 5;
	
	//문제가 되는 점...
	p_a++;
	cout << *p_a << endl;
}


int main() {

	int a = 2;
	cout << "a의 주소값은 ? " << &a << ", a의 값은 : " << a << endl;
	cout << endl;

	change(&a);
	
	return 0;
}

-> 메모리 접근이 가능하다...

  • call by reference
    : 매개변수가 원본의 메모리 자체를 가지고 와서 사용한다.
    원본의 값 변경이 이루어진다.
    call by address에서 무방비한 메모리 접근을 방지한다.
  • 레퍼런스이므로 메모리 접근 방지한다.

#include <iostream>
using namespace std;

void change(int &ref_a)
{
	ref_a = 5;
	cout << "ref_a의 주소값은 ? " << &ref_a << ", ref_a의 값은 : " << ref_a << endl;
	cout << "스코프 외부에서 레퍼런스하더라도 메모리는 동일하다. " << endl;
}


int main() {
	
	int a = 2;

	cout << "a의 주소값은 ? " << &a << ", a의 값은 : " << a << endl;
	change(a);
	cout << "a의 주소값은 ? " << &a << ", a의 값은 : " << a << endl;


	return 0;
}

4. 상수 대입은 불가능하다.

#include <iostream>
using namespace std;


int main() {
	
	int a = 2;

	int &ref_a = 5;


	return 0;
}
  • const 명시하면 가능하다.
    이를 통해서 레퍼런스는 초기화가 이루어지면 읽기만 가능하다는 것을 알 수 있다.

#include <iostream>
using namespace std;


int main() {
	
	int a = 2;

	const int &ref_a = 5;

	return 0;
}

5. 레퍼런스를 반환해보자.

  • 내 컴퓨터에서는 올바르게 나오지만,,, 원래는 런타임에러가 발생한다.

-> 생각을 해보자... 지역변수의 메모리를 가지고 와서 a값에 넣어준다는 것인데
ref_func의 a의 경우는 지역변수이므로 함수 끝마치면 메모리가 없어지는데
메모리를 반환받는다는 것이 말이 안된다.

-> 이와 같이 원래 참조 하던 것이 사라진 레퍼런스를 댕글링 레퍼런스라고 한다.

  • 하지만 지속성을 const를 이용해 끌고 나갈수 있다.
    원칙상 함수 리턴후 지역변수 소멸되어야 하는 것이 맞지만, 리턴값의 생명이 연장된다?? 근데 메모리는 없어지는 듯... 좀 이상한데...

#include <iostream>
using namespace std;

int ref_func()
{
	int a = 2;
	cout << "a의 값은 : " << a << ",a의 주소는 : " << &a << endl;

	return a;
}


int main() {
	
	const int& a = ref_func();
		
	cout << "a의 값은 : " << a << ",a의 주소는 : " << &a << endl;

	return 0;
}
profile
🔥🔥🔥

0개의 댓글