1. 레퍼런스의 개념
변수가 참조로 선언되면 기존 변수의 대체 명칭이 된다. 이때 선언문에 &를 넣어 변수를 참조로 선언할 수 있다.
또한, 우리는 기준 변수를 다른 변수의 기준이 될 수 있는 변수의 한 종류로 정의할 수 있다. &는 변수 또는 메모리의 주소를 나타내는 데 사용된다. 기준 변수와 관련된 변수는 이름을 사용하거나 그와 관련된 기준 변수를 사용할 수 있다.
구문:
data_type &ref = variable;
예시:
// C++ Program to demonstrate
// use of references
#include <iostream>
using namespace std;
int main()
{
int x = 10;
// ref is a reference to x.
int& ref = x;
// Value of x is now changed to 20
ref = 20;
cout << "x = " << x << '\n';
// Value of x is now changed to 30
x = 30;
cout << "ref = " << ref << '\n';
return 0;
}
출력:
x = 20
ref = 30
2. C++에서 레퍼런스의 응용
C++에서는 여러 개의 참조 응용이 있으며, 그것들은 다음을 포함한다.
① 함수에서 전달된 매개 변수 수정
함수가 변수에 대한 참조를 받으면 변수의 값을 수정할 수 있다. 예를 들어, 다음의 프로그램 변수는 참조를 사용하여 스왑된다:
// C++ Program to demonstrate
// Passing of references as parameters
#include <iostream>
using namespace std;
// Function having parameters as
// references
void swap(int& first, int& second)
{
int temp = first;
first = second;
second = temp;
}
// Driver function
int main()
{
// Variables declared
int a = 2, b = 3;
// function called
swap(a, b);
// changes can be seen
// printing both variables
cout << a << " " << b;
return 0;
}
출력:
3 2
② 대형 구조물 복사 방지
큰 개체를 받아야 하는 함수를 생각해 보자. 만약 우리가 그것을 참조 없이 넘기면, 그것의 새로운 복사본이 만들어지고, 이것은 CPU 시간과 메모리의 낭비를 야기할 것이다. 우리는 이런 상황을 피하기 위해 참조를 사용할 수 있다.
예시:
struct Student {
string name;
string address;
int rollNo;
}
// If we remove & in below function, a new
// copy of the student object is created.
// We use const to avoid accidental updates
// in the function as the purpose of the function
// is to print s only.
void print(const Student &s)
{
cout << s.name << " " << s.address << " " << s.rollNo
<< '\n';
}
③ 각 루프에서 모든 개체 수정
각 루프에 대한 참조를 사용하여 모든 요소를 수정할 수 있다.
예시:
// C++ Program for changing the
// values of elements while traversing
// using references
#include <iostream>
#include <vector>
using namespace std;
// Driver code
int main()
{
vector<int> vect{ 10, 20, 30, 40 };
// We can modify elements if we
// use reference
for (int& x : vect) {
x = x + 5;
}
// Printing elements
for (int x : vect) {
cout << x << " ";
}
cout << '\n';
return 0;
}
출력:
15 25 35 45
④ 각 루프에서 개체의 복사 방지
각 루프에서 참조를 사용하여 개체가 클 때 개별 개체의 복사를 피할 수 있다.
예시:
// C++ Program to use references
// For Each Loop to avoid the
// copy of objects
#include <iostream>
#include <vector>
using namespace std;
// Driver code
int main()
{
// Declaring vector
vector<string> vect{ "gutilog zzang",
"gutilog zzang",
"gutilog man" };
// We avoid copy of the whole string
// object by using reference
for (const auto& x : vect) {
cout << x << '\n';
}
return 0;
}
출력:
gutilog zzang
gutilog zzang
gutilog man
3. 포인터 VS 레퍼런스
참조와 포인터 모두 다른 함수 안에서 한 함수의 로컬 변수를 변경하는 데 사용할 수 있다. 두 가지 모두 함수에 인수로 전달되거나 함수에서 돌아왔을 때 큰 개체의 복사를 저장하여 효율성을 얻는 데 사용할 수도 있다. 그러나 이런 유사점에도 불구하고 참조와 포인터 사이에는 다음과 같은 차이점이 있다.
① 포인터는 void로 선언할 수 있지만 참조는 절대 void로 할 수 없다. 예를 들면 다음과 같다.
int a = 10;
void* aa = &a; // it is valid
void& ar = a; // it is not valid
② 포인터 변수에는 단일 포인터, 이중 포인터, 삼중 포인터와 같은 n-레벨/복수 레벨의 간접 정보가 있다. 반면, 참조 변수에는 단일 레벨의 간접 정보만 있다.
③ 참조 변수는 업데이트될 수 없다.
④ 참조 변수는 내부 포인터다.
⑤ 참조 변수 선언은 & 기호 앞에 붙는다. 그러나 이것을 주소로 읽지는 않는다.
예시:
// C++ Program to demonstrate
// references and pointers
#include <iostream>
using namespace std;
// Driver Code
int main()
{
// simple or ordinary variable.
int i = 10;
// single pointer
int* p = &i;
// double pointer
int** pt = &p;
// triple pointer
int*** ptr = &pt;
// All the above pointers differ in the value they store
// or point to.
cout << "i = " << i << "\t"
<< "p = " << p << "\t"
<< "pt = " << pt << "\t"
<< "ptr = " << ptr << '\n';
// simple or ordinary variable
int a = 5;
int& S = a;
int& S0 = S;
int& S1 = S0;
// All the references do not differ in their
// values as they all refer to the same variable
cout << "a = " << a << "\t"
<< "S = " << S << "\t"
<< "S0 = " << S0 << "\t"
<< "S1 = " << S1 << '\n';
return 0;
}
출력:
i = 10 p = 0x7ffecfe7c07c pt = 0x7ffecfe7c080 ptr = 0x7ffecfe7c088
a = 5 S = 5 S0 = 5 S1 = 5
4. 레퍼런스의 이점
① 참조를 초기화해야 하므로 와일드 포인터와 같은 와일드 참조는 존재하지 않을 것이다. 그리고 여전히 유효한 위치를 참조하지 않는 참조를 가질 수 있다.
② 참조는 값에 접근하기 위해 디레퍼런스 연산자가 필요하지 않다. 그리고 일반 변수처럼 사용할 수 있다. & 연산자는 선언할 때만 필요하다. 또한, 객체 참조의 구성원은 구성원에 접근하기 위해 화살표 연산자(->)가 필요한 포인터와 달리, 점 연산자(.)로 접근할 수 있다.
5. 레퍼런스의 한계
① 일단 참조가 만들어지면, 나중에 다른 개체를 참조하도록 만들 수 없고, 재설정할 수 없다. 이것은 포인터로 수행되는 경우가 많다.
② 참조는 NULL일 수 없다. 포인터는 유효한 것을 가리키고 있지 않음을 나타내기 위해 종종 NULL로 만들어기도 하지만 말이다.
③ 선언할 때 참조를 초기화해야 한다. 포인터에는 이러한 제한이 없다.
위와 같은 제한 때문에 C++의 참조는 Linked List, Tree 등과 같은 데이터 구조를 구현하는 데 사용할 수 없다. 자바에서야 참조는 위와 같은 제한이 없으며 모든 데이터 구조를 구현하는 데 사용할 수 있다. 그래서 자바에서 포인터가 필요하지 않은 주된 이유는 자바에서 참조가 강력하기 때문이다.
'C++' 카테고리의 다른 글
C++ 다차원 배열 총정리 (0) | 2024.05.26 |
---|---|
C++ 배열(Arrays) 총정리 (0) | 2024.05.22 |
C++ 포인터 응용 (0) | 2024.05.17 |
C++ 댕글링, 보이드, 널, 와이드 포인터 (0) | 2024.05.16 |
C++ 포인터 산술(Pointer Arithmetic) (1) | 2024.05.01 |