C++에서 ref를 사용하는 이유
by Ungbae
C++에서 ref를 사용하는 이유는? pointer를 사용하면 되지 않나?

참조자가 더 안전하고 편리하다.
포인터로 할 수 있는 것들을 참조자로도 할 수 있지만, 참조자가 더 간단하고 실수를 줄여준다.
일단 문법이 훨씬 간단하다.
void swap(int *x, int *y) {
int temp = *x; // * 붙여야 함
*x = *y; // * 붙여야 함
*y = temp; // * 붙여야 함
}
int main() {
int a = 5, b = 10;
swap(&a, &b); // & 붙여서 주소 전달해야 함
}
하지만 참조자의 경우 코드를 보자
void swap(int &x, int &y) {
int temp = x; // 그냥 쓰면 됨
x = y; // 그냥 쓰면 됨
y = temp; // 그냥 쓰면 됨
}
int main() {
int a = 5, b = 10;
swap(a, b); // 그냥 전달
}
훨씬 직관적이고 실수할 가능성이 적다.
두 번째로 NULL 포인터 문제가 없다.
void print(int *ptr) {
cout << *ptr << endl; // ptr이 nullptr이면? 충돌 발생
}
int main() {
int *p = nullptr;
print(p); // 위험. 프로그램 종료
}
그래서 항상 nullptr 체크를 해야 한다.
void print(int *ptr) {
if (ptr != nullptr) { // 매번 체크 필요
cout << *ptr << endl;
}
}
반면에 참조자는 안전하다.
void print(int &ref) {
cout << ref << endl; // 항상 안전. nullptr 불가능
}
int main() {
int x = 10;
print(x); // 안전
// print(nullptr); // 컴파일 에러. 애초에 불가능
}
참조자는 반드시 유효한 객체를 가리켜야 하기 때문에 더 안전하다.
세 번째로 읽기 쉬운 코드를 작성할 수 있다.
void calculate(int *a, int *b, int *result) {
*result = (*a) + (*b); // * 많아서 읽기 어려움
}
int main() {
int x = 5, y = 10, res;
calculate(&x, &y, &res); // & 붙이는 것도 번거로움
}
하지만 참조자는
void calculate(int a, int b, int &result) {
result = a + b; // 간단명료
}
int main() {
int x = 5, y = 10, res;
calculate(x, y, res); // 자연스러움
}
그리고 연산자 오버로딩에 필수이다.
class Vector {
int x, y;
public:
// [] 연산자 오버로딩 - 참조 반환 필수!
int& operator[](int index) {
if (index == 0) return x;
return y;
}
};
int main() {
Vector v;
v[0] = 10; // 이렇게 쓰려면 참조 반환이 필수!
v[1] = 20;
}
참조자 없이는 이렇게 불가능한 것들이 있다.
포인터로는 이렇게 자연스러운 문법을 만들 수 없다.
const의 정확성도 한 몫한다(Const Correctness).
포인터의 경우
void print(const int *ptr) { // 값은 못 바꿈
*ptr = 10; // 에러
ptr = nullptr; // 포인터 자체는 바꿀 수 있음
}
참조자의 경우
void print(const int &ref) { // 값은 못 바꿈
ref = 10; // 에러
// ref는 다른 걸 가리킬 수 없음 (더 안전)
}
그리고 참조자를 사용하면 큰 객체 전달을 하게 될 때 효율적이다.
큰 객체를 값 복사를 하게되면 매우 느리다.
void process(string str) { // 문자열 전체 복사
cout << str << endl;
}
포인터를 사용하면 복잡하다.
void process(const string *str) { // 복사 안함
if (str != nullptr) { // 체크 필요
cout << *str << endl; // * 붙여야 함
}
}
참조자가 GOAT
void process(const string &str) { // 복사 안함, 간단함
cout << str << endl;
}
그럼 포인터는 언제 쓰라는거냐
포인터가 필요한 경우에 쓰면 된다. (당연한 얘기)
nullptr가 의미있는 경우가 이에 해당한다.
int* findValue(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
return &arr[i]; // 찾으면 주소 반환
}
}
return nullptr; // 못 찾으면 nullptr
}
포인터를 재할당하는 경우에도 포인터를 쓴다.
int a = 10, b = 20;
int *ptr = &a;
ptr = &b; // OK 다른 곳을 가리킬 수 있음
int &ref = a;
ref = b; // ref가 b를 가리키는 게 아니라 a의 값이 바뀜
동적 메모리를 할당할 때에도
int *arr = new int[100]; // 포인터 필요
delete[] arr;
배열이나 C 스타일의 문자열에서도
char *str = "Hello";
int *arr = new int[10];
클로드의 조언
- 일반적으로는 참조자를 먼저 고려하라
- 함수 매개 변수로 큰 객체를 전달할 때에는 const & 를 사용할 것
- 함수에서 값 수정할 때에는 &를 사용할 것
- nullptr가 의미있는 경우만 포인터를 사용할 것
'CS & AI > OOP with C++' 카테고리의 다른 글
| 5. Memory Allocation, Qualifiers and Operator Overloading (0) | 2025.10.21 |
|---|---|
| 4. Class, Function Overloading, and Constructor / Destructor (0) | 2025.10.19 |
| 3. Introduction to C++ (1) | 2025.10.17 |
| 2. Understanding Object Oriented Design Concepts (0) | 2025.10.16 |
| 1. Programming and Real World Modeling (0) | 2025.09.17 |
블로그의 정보
그럼에도 불구하고
Ungbae