포인터 vs 참조

 

인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다. 
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!

 

 


 

 포인터 vs 참조

 

성능 : 포인터와 참조 똑같다

편의성 : 참조 승!


 

1. 편의성 관련

 

편의성 관련

-  편의성이 좋다는게 꼭 장점만은 아니다.

-  포인터는 주소를 넘기니 확실하게 원본을 넘긴다는 힌트를 줄 수 있는데
-  참조는 자연스럽게 모르고 지나칠 수도 있음!
- ex) 마음대로 고친다면?

  • const를 사용해서 이런 마음대로 고치는 부분 개선 가능
void PrintInfoByRef(const StatInfo& info)
{
    cout << "-------------------- " << endl;
    cout << "HP: " << info.hp << endl;
    cout << "ATT: " << info.attack << endl;
    cout << "DEF: " << info.defence << endl;
    cout << "-------------------- " << endl;
}
// 신입이 왔다
// info.hp = 10000;  실수로 다음과 같이 기입해도 에러가 뜬다.

 

  • 참고로 포인터도 const를 사용 가능
         * 기준으로 앞에 붙이느냐, 뒤에 붙이느냐에 따라 의미가 달라진다.


-   const 별 에 붙인다면?

  • StatInfo* const info
  • info라는 바구니의 내용물(주소)을 바꿀 수 없음
  • info는 주소값을 갖는 바구니 -> 이 주소값이 고정이다!
  • 데이터는 바꿀 수 있고 주솟값은 바꿀 수 없다. 
  • info[주소값]     주소값[데이터]
    • info = &globalInfo;  (O)
    • info->hp = 10000;   (X)

 

 -   const 별 이전에 붙인다면?   

  • const StatInfo* info
  • StatInfo const* info
  • 위의 두가지 표현은 같은 의미다.
  • info가 '가리키고 있는' 바구니의 내용물을 바꿀 수 없음
  • '원격' 바구니의 내용물을 바꿀 수 없음.
  • 주소값은 바꿀 수 있고 데이터는 바꿀 수 없다.  
  • info[주소값]     주소값[데이터]
    • info = &globalInfo;  (O)
    • info->hp = 10000;   (X)

    

info[주소값]     주소값[데이터]

 

  const 별 에 붙인다면? const 별 이전에 붙인다면?   
 
  • StatInfo* const info


  • info라는 바구니의 내용물(주소)을 바꿀 수 없음
  • info는 주소값을 갖는 바구니 -> 이 주소값이 고정이다!
  • 데이터는 바꿀 수 있고 주솟값은 바꿀 수 없다. 

  • info[주소값]     주소값[데이터]
    • info = &globalInfo;  (O)
    • info->hp = 10000;   (X)
  • const StatInfo* info
  • StatInfo const* info
  • 위의 두가지 표현은 같은 의미다.
  • info가 '가리키고 있는' 바구니의 내용물을 바꿀 수 없음
  • '원격' 바구니의 내용물을 바꿀 수 없음.
  • 주소값은 바꿀 수 있고 데이터는 바꿀 수 없다.  

  • info[주소값]     주소값[데이터]
    • info = &globalInfo;  (O)
    • info->hp = 10000;   (X)

 

 

 

둘 다 막는 경우

const StatInfo* const info는 둘 다 바꿀 수 없다.

// [매개변수][RET][지역변수(info)] [매개변수(&info)][RET][지역변수]
void PrintInfoByPtr(const StatInfo* const info)
{
    if (info == nullptr)
        return;

    cout << "-------------------- " << endl;
    cout << "HP: " << info->hp << endl;
    cout << "ATT: " << info->attack << endl;
    cout << "DEF: " << info->defence << endl;
    cout << "-------------------- " << endl;
}

 

 


 

 

 

2.  초기화 여부

 

초기화 여부

-   참조 타입은 바구니의 2번째 이름 (별칭?)

  • 참조하는 대상이 없으면 안됨
  • 참조 타입은 이런 nullptr과 같은 개념이 없다. 공허한 값을 가리킬 수 없다.

 

-   반면 포인터는 그냥 어떤~ 주소라는 의미

  • 대상이 실존하지 않을 수도 있음
  • 포인터에서 '없다'는 의미로?
    • nullptr
      • 공허한 값을 가리킨다.
      • nullptr인 상황에서 해당 주소에 접근하려하면 크러쉬가 난다.

 

 StatInfo* pointer = nullptr;
 pointer = &info;
 PrintInfoByPtr(pointer);

/* StatInfo& reference;  참조하는 대상이 없다. 불가능한 표기
   reference = info; */  
 StatInfo& reference = info;  // 참조 타입은 다음과 같이 표기. 위와 같이 표기가 불가능하다.
 PrintInfoByRef(reference);

 

 

 

그래서 참조와 포인터 중 어떤걸 사용하는게 좋은가?  

 

-  사실 Team By Team... 정해진 답은 없다.

  • ex) 구글에서 만든 오픈소스를 보면 거의 무조건 포인터 사용
  • ex) 언리얼 엔진에선 reference도 애용

-  없는 경우도 고려해야 한다면 pointer (null 체크 필수)

 

-  바뀌지 않고 읽는 용도(read only)만 사용하면 const ref&

 

-  그 외 일반적으로 reference (명시적으로 호출할 때 OUT을 붙인다)

-  단, 다른 사람이 pointer로 만들어 놓은걸 이어서 만든다면, 계속 pointer (섞어 사용하지 않는다)

 

 

#include <iostream>
using namespace std;

//...

StatInfo globalInfo;
// [매개변수][RET][지역변수(info)] [매개변수(&info)][RET][지역변수]
void PrintInfoByPtr(const StatInfo* const info)
{
    if (info == nullptr)
        return;

    cout << "HP: " << info->hp << endl;
    cout << "ATT: " << info->attack << endl;
    cout << "DEF: " << info->defence << endl;
}    

void PrintInfoByRef(const StatInfo& info)
{
    cout << "HP: " << info.hp << endl;
    cout << "ATT: " << info.attack << endl;
    cout << "DEF: " << info.defence << endl;
}

#define OUT    
void ChangeInfo(OUT StatInfo& info)
{
    info.hp = 1000;    
}

int main()
{
    StatInfo* pointer = nullptr;
    pointer = &info;
    PrintInfoByPtr(pointer);

    StatInfo& reference = info;  // 참조 타입은 다음과 같이 표기. 위와 같이 표기가 불가능하다.
    PrintInfoByRef(reference);

    ChangeInfo(OUT info);

    // 보너스) 포인터로 사용하던걸 참조로 넘겨주려면?
    // pointer[ 주소(&info) ]  info[ 데이터 ]
    PrintInfoByRef(*pointer);  
    // 위의 것을 풀어쓰면 아래와 같다. 
    // StatInfo& ref = *pointer;
	// PrintInfoByRef(ref)

    // 보너스) 참조로 사용하던걸 포인터로 넘겨주려면?
    // pointer[ 주소 ]  reference, info[ 데이터 ]
    PrintInfoByPtr(&reference);  
    // 위의 것을 풀어쓰면 아래와 같다. 
    // StatInfo* ptr = &reference;
    // PrintInfoByPtr(ptr)

    return 0;
}

 


 

 

코드

 

더보기
#include <iostream>
using namespace std;

struct StatInfo
{
    int hp;  // +0
    int attack;  // +4
    int defence;  // +8
};

// [매개변수][RET][지역변수(info)] [매개변수(&info)][RET][지역변수]
void CreateMonster(StatInfo* info)
{
    info->hp = 100;
    info->attack = 8;
    info->defence = 5;
}

// 특정 조건을 만족하는 몬스터를 찾는 함수
StatInfo* FindMonster()
{
    // TODO : Heap 영역에서 뭔가를 찾아봄
    // 찾았다!
    // return monster~;

    return nullptr;  // 못 찾았다
}

StatInfo globalInfo;
// [매개변수][RET][지역변수(info)] [매개변수(&info)][RET][지역변수]
void PrintInfoByPtr(const StatInfo* const info)
{
    if (info == nullptr)
        return;

    cout << "-------------------- " << endl;
    cout << "HP: " << info->hp << endl;
    cout << "ATT: " << info->attack << endl;
    cout << "DEF: " << info->defence << endl;
    cout << "-------------------- " << endl;

    // const 별 뒤에 붙인다면?
    // StatInfo* const info
    // info라는 바구니의 내용물(주소)을 바꿀 수 없음
    // info는 주소값을 갖는 바구니 -> 이 주소값이 고정이다!
    
    // const 별 이전에 붙인다면?
    // const StatInfo* info
    // info가 '가리키고 있는' 바구니의 내용물을 바꿀 수 없음
    // '원격' 바구니의 내용물을 바꿀 수 없음

    // info[주소값]     주소값[데이터]
    // info = &globalInfo;
    // info->hp = 10000;
    // const StatInfo* const info는 둘 다 바꿀 수 없다.

}    

void PrintInfoByRef(const StatInfo& info)
{
    cout << "-------------------- " << endl;
    cout << "HP: " << info.hp << endl;
    cout << "ATT: " << info.attack << endl;
    cout << "DEF: " << info.defence << endl;
    cout << "-------------------- " << endl;
}
// 신입이 왔다
// info.hp = 10000;  실수로 다음과 같이 기입해도 에러가 뜬다.

#define OUT    
void ChangeInfo(OUT StatInfo& info)
{
    info.hp = 1000;    
}

int main()
{
    StatInfo info;
    CreateMonster(&info);

    // 포인터 vs 참조 세기의 대결
    // 성능 : 똑같음!
    // 편의성 : 잠조 승!

    // 1) 편의성 관련
    // 편의성이 좋다는게 꼭 장점만은 아니다.
    // 포인터는 주소를 넘기니 확실하게 원본을 넘긴다는 힌트를 줄 수 있는데
    // 참조는 자연스럽게 모르고 지나칠 수도 있음!
    // ex) 마음대로 고친다면?
    // const를 사용해서 이런 마음대로 고치는 부분 개선 가능

    // 참고로 포인터도 const를 사용 가능
    // * 기준으로 앞에 붙이느냐, 뒤에 붙이느냐에 따라 의미가 달라진다.

    // 2) 초기화 여부
    // 참조 타입은 바구니의 2번째 이름 (별칭?)
    // -> 참조하는 대상이 없으면 안됨
    // 반면 포인터는 그냥 어떤~ 주소라는 의미
    // -> 대상이 실존하지 않을 수도 있음
    // 포인터에서 '없다'는 의미로? nullptr
    // 참조 타입은 이런 nullptr

    StatInfo* pointer = nullptr;
    pointer = &info;
    PrintInfoByPtr(pointer);

    /* StatInfo& reference;  참조하는 대상이 없다. 불가능한 표기
    reference = info; */  
    StatInfo& reference = info;  // 참조 타입은 다음과 같이 표기. 위와 같이 표기가 불가능하다.
    PrintInfoByRef(reference);

    // 그래서 결론은?
    // 사실 Team By Team... 정해진 답은 없다
    // ex) 구글에서 만든 오픈소스를 보면 거의 무조건 포인터 사용
    // ex) 언리얼 엔진에선 reference도 애용

    // 없는 경우도 고려해야 한다면 pointer (null 체크 필수)
    // 바뀌지 않고 읽는 용도(read only)만 사용하면 const ref&
    // 그 외 일반적으로 ref (명시적으로 호출할 때 OUT을 붙인다)
    // 단, 다른 사람이 pointer로 만들어 놓은걸 이어서 만든다면, 계속 pointer (섞어 사용하지 않는다)

    ChangeInfo(OUT info);

    // 보너스) 포인터로 사용하던걸 참조로 넘겨주려면?
    // pointer[ 주소(&info) ]  info[ 데이터 ]
    PrintInfoByRef(*pointer);  // 다른식의 표기 : StatInfo& ref = *pointer;

    // 보너스) 참조로 사용하던걸 포인터로 넘겨주려면?
    // pointer[ 주소 ]  reference, info[ 데이터 ]
    PrintInfoByPtr(&reference);  // 다른식의 표기 : StatInfo* ptr = &reference;

    return 0;
}

 

 

 

'⭐ Programming > C++' 카테고리의 다른 글

[C++] 포인터 vs 배열  (0) 2022.03.26
[C++] 배열 기초  (0) 2022.03.26
[C++] 참조 기초  (0) 2022.03.26
[C++] 포인터 실습  (0) 2022.03.26
[C++] 포인터 연산  (0) 2022.03.25