스토리텔링 개발자

[More Effective C++] 1. 포인터 vs 참조자 본문

개발/More Effective C++

[More Effective C++] 1. 포인터 vs 참조자

김디트 2024. 8. 1. 10:51
728x90

항목 1 : 포인터와 참조자를 구분하자

 

 

 

포인터와 참조자의 차이
  • 참조자 개념에선 '널 참조자(null reference)'라는 것이 없다. 즉,
    • 포인터 사용
      • 어떤 변수가 참조하는 부분에 객체가 없을 수도 있는 상황.
    • 참조자 사용
      • 어떤 변수가 가리키는 메모리가 항상 유효한 객체여야 하는 상황
  • C++ 스펙에 의하면 참조자는 선언될 때 반드시 초기화해야 한다.
// 참조자의 경우
string& rs; // 컴파일 에러. 초기화가 없다.

string s("xyzzy");
string& rs = s; // 컴파일 성공.

// 포인터의 경우
string* ps; // 초기화 되지 않은 포인터. 컴파일 성공. 허나, 무슨 값이 들어있을지..
  • 참조자는 중간에 다른 객체를 참조하게 할 수 없다.
    • 포인터의 경우
      • 다른 객체의 주소값으로 얼마든지 바꾸어 셋팅할 수 있다.
    • 참조자의 경우
      • 초기화될 때 참조했던 객체만 참조할 수 있다.
string s1("Nancy");
string s2("Clancy");

string& rs = s1; // 참조자
string* ps = &s1; // 포인터

rs = s2; // ps는 여전히 s1이며, s1의 값에 Clancy가 덮어씌워진다.
ps = &s2; // ps는 s2를 가리키며, s1의 값은 변경되지 않는다.

 

 

 

미정의 동작을 유발하는 코드
char* pc = nullptr;
char& rc = *pc; // 널 포인터를 역참조한 것을 참조자에 할당!
  • 컴파일은 되겠지만, 실행 결과는 예측할 수 없다.
  • 이런 코드를 만들거면 차라리 참조자를 쓰지 말자..

 

 

 

포인터보다는 참조자를 쓰는 것이 더 효율적일 수 있다?
  • 널 참조자가 없다 는 스펙이 참조자가 더 효율적임을 암시한다.
  • 참조자는 쓰기 전에 유효성을 검사할 필요가 없다는 뜻이기 때문이다.
// 참조자의 경우
void printDouble(const double& rd)
{
    // rd 유효성 검사 불필요.
    // rd는 참조자이므로 값이 없을 리 없다.
    coud << rd;
}

// 포인터의 경우
void printDouble(const double* pd)
{
    if(pd) // 널 포인터인지 유효성 검사 필요
    {
        cout << *pd;
    }
}

 

 

 

정리
  • 포인터를 써야 하는 경우
    • 딱히 가리킬 객체의 주소가 없을 때
    • 하나의 변수를 가지고 여러 개의 객체를 바꾸어 참조해야 할 때
  • 참조자를 써야 하는 경우
    • 참조할 포인터가 처음부터 끝까지 존재할 것임을 알고 있을 때
    • 참조하는 대상 객체를 바꿀 필요가 없을 때
    • (특수 상황) 연산자 함수를 구현할 때

 

 

 

operator[] 함수 구현
  • 이 연산자는 대입 연산자의 좌변으로 쓸 수 있도록 값을 반환해 주어야 한다.
  • 즉, 참조자를 리턴하게 된다.
vector<int> v(10);

// 참조자 리턴의 경우
v[5] = 10; // operator[]의 반환값이 operator=의 좌변이 된다.

// 만약 포인터 리턴이라면?
// 사용성이 매우 불편해진다.
*v[5] = 10; // 이런 식으로 사용되어야 할 것이다.
  • 포인터 리턴 형태의 단점
    • *를 달고 사용해야 하므로, 사용성이 불편하다.
    • 더군다나 *은 포인터의 벡터인 것처럼 보이게 만든다.
  • 그러므로 참조자를 반환하도록 하자. (항목 30 참조)

 

 

 

참조
 

[Effective C++] 21. 참조자를 리턴하면 안되는 상황

항목 21. 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자   모든 코드에 '참조에 의한 전달'을 반영하려들면 안 되는 이유class Rational{public: Rational(int numerator = 0, int denominato

delightlane.tistory.com

 

728x90
Comments