본문으로 건너뛰기

참조(reference)

개념

참조(reference)는 C++에서 도입된 개념으로, 이미 존재하는 변수에 대한 별칭(alias) 을 생성하는 기능이다. 참조는 선언 시점에 반드시 초기화되어야 하며, 한번 초기화된 후에는 다른 객체를 참조할 수 없다.

int original = 5; // 원본 변수
int& ref = original; // original에 대한 참조 변수 선언

ref = 10; // original 값도 10으로 변경됨

포인터와의 차이점

초기화 필수성

  • 참조: 선언과 동시에 반드시 초기화해야 한다.
  • 포인터: 선언 시 초기화하지 않아도 됨

재할당 가능성

  • 참조: 한번 초기화되면 다른 객체를 참조할 수 없음
  • 포인터: 다른 주소를 가리키도록 변경 가능

NULL 값

  • 참조: NULL 참조 없음(항상 유효한 객체를 참조해야 함)
  • 포인터: NULL 포인터 가능

연산자 사용

  • 참조: 원본 변수와 동일하게 직접 접근(ref = 10;)
  • 포인터: 역참조 연산자(*)를 사용(*ptr = 10;)

메모리 할당

  • 참조: 일반적으로 별도의 메모리를 할당하지 않음
  • 포인터: 포인터 자체에 메모리가 할당됨

참조 매개변수와 반환 값

참조 매개변수

함수에 인자를 전달할 때 참조를 사용하면 값의 복사 없이 원본 데이터에 직접 접근할 수 있다. 이는 C에서 포인터를 사용하는 이유와 유사하지만, 문법적으로 더 간결하고 안전함

// 값에 의한 전달(pass by value) - 원본 변경 안됨
void incrementByValue(int num) {
num++; // 지역 변수만 변경됨
}

// 참조에 의한 전달(pass by reference) - 원본 변경됨
void incrementByReference(int& num) {
num++; // 원본 변수 값이 변경됨
}

참조 매개변수의 장점

  1. 큰 객체 전달 시 효율성: 큰 객체를 값으로 전달하면 복사 비용이 발생하지만, 참조로 전달하면 복사 없이 원본에 접근
  2. 원본 변경 가능: 함수 내에서 원본 데이터를 직접 수정할 수 있음
  3. 문법적 간결함: 포인터보다 사용하기 쉽고, 코드가 더 읽기 쉬움

참조 반환 값

함수가 참조를 반환하면 함수 호출 표현식이 l-value(변수처럼 값을 할당받을 수 있는 표현식)가 될 수 있다.

int& getElement(vector<int>& vec, int index) {
return vec[index]; // 벡터 요소에 대한 참조 반환
}

vector<int> numbers = {10, 20, 30};

// 함수 반환값에 직접 할당 가능
getElement(numbers, 1) = 50;

// numbers는 이제 {10, 50, 30}

참조 반환의 주의사항

  1. 지역 변수 참조 반환 금지: 함수 내 지역 변수의 참조를 반환하면 함수 종료 후 해당 변수는 소멸되어 댕글링 참조(dangling reference)가 발생
  2. 반환 값의 생명주기: 참조로 반환된 객체의 생명주기가 함수 호출보다 길어야 함

const 참조의 활용

const 참조의 기본 개념

const 참조는 참조를 통해 원본 객체를 수정할 수 없도록 하는 기능이다. 이는 함수 매개변수로 자주 사용되어 원본 데이터의 무결성을 보장한다.

void printData(const int& data) {
// data = 100; // 오류: const 참조를 통해 값을 변경할 수 없음
cout << data << endl; // 읽기만 가능
}

const 참조의 특별한 성질

일반 참조와 달리, const 참조는 다음과 같은 특별한 성질을 가진다.

임시 객체(r-value)에 대한 참조 가능

// 일반 참조는 임시 객체에 바인딩할 수 없음
// int& ref = 5; // 컴파일 오류

// const 참조는 임시 객체에 바인딩 가능
const int& ref = 5;

타입 변환 허용

double value = 3.14;
// int& ref = value; // 컴파일 오류: 타입 불일치

// const 참조는 타입 변환 허용
const int& ref = value; // 내부적으로 임시 int 객체 생성

const 참조의 주요 활용 사례

함수 매개변수

큰 객체를 복사 없이 전달하되 수정은 방지하고 싶을 때 사용

void processString(const string& str) {
// str의 내용은 읽을 수 있지만 수정할 수 없음
}

클래스의 멤버 함수

객체의 상태를 변경하지 않는 멤버 함수(getter 등)는 const 메소드로 선언하고, 내부에서 const 참조를 사용

class Data {
private:
vector<int> values;
public:
// const 멤버 함수에서 멤버 변수를 const 참조로 접근
const vector<int>& getValues() const {
return values; // 안전하게 내부 데이터 참조 반환
}
}

효율적인 범위 기반 for 루프

vector<string> names = {"Alice", "Bob", "Charlie"};

// 효율적인 읽기 전용 반복
for (const string& name : names) {
cout << name << endl;
// name = "Modified"; // 컴파일 오류: 수정 불가
}

참조의 실용적 활용 사례

값과 참조 중 선택 기준:

  • 작은 기본 타입(int, double 등)은 값으로 전달
  • 큰 객체나 사용자 정의 타입은 참조로 전달(const 참조 권장)
  • 원본 수정이 필요한 경우에만 비-const 참조 사용

반환 값으로서의 참조

  • 클래스 내부 멤버에 대한 접근자 함수 사용
  • 연산자 오버로딩에서 체이닝을 위해 사용(obj1 = obj2 = obj3)
  • 지역 변수의 참조 반환은 절대 금지

r-value 참조와 이동 의미론

  • C++11부터 도입된 r-value 참조(&&)는 임시 객체를 효율적으로 활용하기 위한 기능
  • 이동 생성자와 이동 대입 연산자에서 주로 사용
class MyString {
public:
// 이동 생성자(r-value 참조 사용)
MyString(Mystring && other) noexcept {
// other의 리소스를 훔쳐옴
}
}

요약

C++의 참조는 포인터의 강력함과 일반 변수의 사용 편의성을 결합한 기능이다. 참조는 코드를 더 직관적이고 안전하게 만들어 주며, 특히 함수 매개변수와 반환 값에서 효율성과 가독성을 높여준다. const 참조의 활용은 C++의 핵심 기법 중 하나로, 큰 객체를 효율적으로 전달하면서도 무결성을 보장할 수 있게 해준다.