C++

C++ 레퍼런스(참조, Reference) 총정리

김구티2 2024. 5. 18. 10:02

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 등과 같은 데이터 구조를 구현하는 데 사용할 수 없다. 자바에서야 참조는 위와 같은 제한이 없으며 모든 데이터 구조를 구현하는 데 사용할 수 있다. 그래서 자바에서 포인터가 필요하지 않은 주된 이유는 자바에서 참조가 강력하기 때문이다.

 

728x90

'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