메뉴 닫기

C++ 함수 호출 방식, 값 전달과 참조 전달의 차이점과 활용법


C++ 함수 호출 방식, 값 전달과 참조 전달의 차이점과 활용법

📌 복사냐 원본 조작이냐? C++ 개발자가 꼭 알아야 할 함수 전달 방식 비교!

안녕하세요. 😊
오늘은 많은 C++ 입문자와 중급 개발자분들이 헷갈려하는 값 전달(pass by value)참조 전달(pass by reference)의 차이점에 대해 이야기해보려 합니다.
두 방식은 함수에 인자를 넘길 때의 동작 방식이 완전히 달라 성능과 결과 모두에 큰 영향을 줄 수 있죠.
그만큼 제대로 이해하지 않으면 디버깅에 많은 시간을 허비하게 되거나, 예기치 않은 결과를 만들 수 있습니다.
함수 호출을 최적화하고, 코드를 더 견고하게 만들고 싶은 분들이라면 오늘 내용 꼭 집중해 주세요!
그럼, 본격적으로 시작해볼까요?

C++에서 함수 호출 시 인자를 전달하는 방법에는 크게 두 가지가 있습니다.
값 전달은 말 그대로 인자의 복사본을 함수에 넘기기 때문에 원본 데이터에는 영향을 주지 않죠.
반면, 참조 전달은 실제 원본 데이터를 넘기기 때문에 함수 내부에서 변경이 가능합니다.
이러한 차이로 인해 성능, 메모리 사용, 코드 안정성까지 영향을 미치게 되는데요.
특히 객체를 다룰 때는 이 두 방식의 차이가 훨씬 더 두드러지게 나타납니다.
이 글에서는 두 방식의 동작 원리부터, 실제 코드 예제, 언제 어떤 방식을 선택해야 하는지에 대한 기준까지 낱낱이 파헤쳐 보겠습니다.







🔗 값 전달과 참조 전달의 기본 개념

C++에서 함수를 호출할 때 가장 먼저 고려해야 할 것은 인자를 어떻게 전달하느냐입니다.
가장 기본적인 방식은 값 전달(pass by value)이며, 그에 비해 참조 전달(pass by reference)은 보다 유연하고 강력한 방식입니다.

📌 값 전달 (Pass by Value)

값 전달은 말 그대로 원본 데이터를 복사해서 함수에 전달하는 방식입니다.
이 말은, 함수 내부에서 해당 인자를 수정하더라도 원래의 값에는 영향을 주지 않는다는 뜻이죠.
간단한 타입(int, double 등)에서는 이 방식이 직관적이며 안전하게 느껴질 수 있습니다.

CODE BLOCK
void changeValue(int x) {
    x = 100;
}

int main() {
    int a = 10;
    changeValue(a);
    // a는 여전히 10
}

위 예제에서 xa의 복사본이며, 함수 내에서 x를 변경해도 a에는 영향을 주지 않습니다.

📌 참조 전달 (Pass by Reference)

참조 전달은 복사 없이 변수의 실제 메모리 주소를 함수에 넘기는 방식입니다.
즉, 함수 안에서 그 값을 직접 조작할 수 있어 실제 원본 데이터가 변경됩니다.
성능 측면에서도 복사가 생략되기 때문에 유리할 수 있습니다.

CODE BLOCK
void changeValue(int &x) {
    x = 100;
}

int main() {
    int a = 10;
    changeValue(a);
    // a는 이제 100
}

이처럼 & 연산자를 통해 참조를 넘기면, 함수 내부에서 변수의 값을 직접 변경할 수 있습니다.

💡 TIP: 참조 전달은 대용량 객체를 넘길 때 복사 비용을 줄일 수 있고, 함수에서 직접 원본을 수정해야 할 경우 유용합니다.


🛠️ 실제 코드 예제로 비교해보기

이론만으로는 개념이 추상적으로 느껴질 수 있기 때문에, 실제 코드 예제를 통해 값 전달과 참조 전달의 차이를 보다 명확히 비교해보겠습니다.
이번에는 벡터(std::vector)와 같은 객체 타입을 사용해서 두 방식의 차이점을 살펴보죠.

📌 값 전달로 객체를 넘긴 경우

CODE BLOCK
#include <iostream>
#include <vector>

void addElement(std::vector<int> vec) {
    vec.push_back(100);
}

int main() {
    std::vector<int> numbers = {1, 2, 3};
    addElement(numbers);
    std::cout << "Size: " << numbers.size() << std::endl; // 출력: Size: 3
}

위 코드에서 numbers값으로 복사된 뒤 함수에 전달되기 때문에, addElement 함수 안에서 push_back()을 하더라도 원본에는 아무런 영향을 미치지 않습니다.

📌 참조 전달로 객체를 넘긴 경우

CODE BLOCK
void addElement(std::vector<int>& vec) {
    vec.push_back(100);
}

위 예제처럼 &를 붙여 참조로 넘기면, 함수 내부의 vecnumbers 그 자체가 되어,
push_back() 호출 시 원본 벡터에 직접 변경이 적용됩니다.

💎 핵심 포인트:
객체는 크기가 크고 복사 비용이 높기 때문에, 특별한 이유가 없다면 참조 전달이 성능과 메모리 측면에서 더 효율적입니다.

하지만 참조 전달은 원본을 수정할 수 있기 때문에 부작용(side effect)이 발생할 수 있다는 점도 반드시 유의해야 합니다.







⚙️ 어떤 방식이 더 빠를까? 성능 차이 분석

값 전달과 참조 전달의 가장 큰 차이점 중 하나는 바로 성능입니다.
특히 C++처럼 성능이 중요한 언어에서는 이 차이를 무시할 수 없죠.
단순 변수일 경우 차이가 미미할 수 있지만, 객체나 대용량 컨테이너에서는 결과가 완전히 달라집니다.

📌 복사 비용이 성능에 미치는 영향

값 전달은 인자를 함수로 넘길 때마다 복사 과정이 발생합니다.
이는 단순한 int나 char 같은 기본 타입에서는 문제가 되지 않지만,
std::vector, std::string, std::map과 같은 객체에서는 복사 자체가 많은 리소스를 소모하게 됩니다.

💬 값 전달이 많아질수록 캐시 미스가 증가하고, 전체 처리 속도 저하로 이어질 수 있습니다.

📌 참조 전달은 빠르지만 주의도 필요

참조 전달은 복사가 없기 때문에 속도 면에서는 훨씬 빠르며 메모리 효율도 높습니다.
그러나 함수 내부에서 실수로 원본 데이터를 변경할 위험이 있다는 점을 잊지 마세요.
즉, 빠르다고 해서 무조건 참조를 쓰기보다는 함수의 목적에 따라 적절히 선택해야 합니다.

  • 변경이 필요 없는 경우라면 값 전달을 통해 데이터 보호
  • 🚀무거운 객체나 고성능이 필요한 경우는 참조 전달로 최적화
  • 🔒참조 전달 시에는 const 참조를 활용하면 안정성과 성능을 동시에 확보

결론적으로, “무조건 참조가 빠르다”는 말은 사실이지만,
“항상 참조가 옳다”는 말은 아닙니다.
상황에 따라 신중히 선택하는 것이 진짜 고수의 선택입니다.


🔌 객체 전달 시 주의해야 할 점

객체를 함수에 전달할 때는 값 전달이냐 참조 전달이냐에 따라 메모리 복사 비용뿐 아니라 동작의 안정성에도 영향을 줍니다.
특히 std::vector, std::string, 사용자 정의 클래스 등 복사 생성자나 소멸자를 포함하는 객체는 조심스럽게 다뤄야 합니다.

📌 깊은 복사 vs 얕은 복사

값 전달을 사용할 경우, 클래스 내부에서 얕은 복사(shallow copy)만 처리되도록 설계되었다면 예기치 않게 포인터 충돌이나 메모리 해제 오류가 발생할 수 있습니다.
이러한 객체는 되도록 참조 전달로 넘기는 것이 안전하며, const & 형태로 사용하면 변경도 방지할 수 있어 유용합니다.

CODE BLOCK
void printName(const std::string& name) {
    std::cout << name << std::endl;
}

이처럼 const std::string& 형태는 복사를 피하면서도 함수 안에서 안전하게 읽기 전용으로 활용할 수 있습니다.

📌 RVO, std::move 같은 최신 C++ 기능 고려

C++11 이후에는 RVO(Return Value Optimization)std::move 같은 기능 덕분에 값 전달도 더 효율적으로 작동할 수 있게 되었습니다.
하지만 여전히 함수의 목적이 원본 조작이라면, 참조를 사용하는 것이 명확성과 성능 모두에서 우위에 있습니다.

⚠️ 주의: 복사 생성자가 명확하지 않은 사용자 정의 객체를 값 전달로 넘기면 예상치 못한 동작이 발생할 수 있습니다.

요약하자면, 객체를 다룰 때는 참조 전달을 기본값으로 고려하고,
정말 복사해도 무방한 경우에만 값 전달을 선택하는 습관이 바람직합니다.







💡 어떤 상황에서 어떤 방식이 적절할까?

함수 호출 시 값 전달과 참조 전달 중 어느 쪽이 더 적절한지를 판단하는 기준은 단순히 성능 하나로 결정되지 않습니다.
함수의 목적, 전달되는 인자의 크기, 수정 여부, 안정성 등을 종합적으로 고려해야 합니다.

📌 값 전달이 적합한 경우

  • 🔹변경이 함수 외부에 영향을 주면 안 될 때
  • 🔹인자의 크기가 작아 복사 비용이 미미할 때
  • 🔹불변성을 보장하고 싶은 경우

📌 참조 전달이 적합한 경우

  • 함수 내부에서 원본을 수정해야 할 때
  • 인자가 복사하기에 너무 크거나 무거울 때
  • 클래스, 벡터, 맵 등의 컨테이너 객체를 다룰 때

📌 const 참조를 적극 활용하자

참조 전달을 사용할 때 const 키워드를 붙이면, 값을 수정하지 못하도록 하면서도 복사를 피할 수 있습니다.
이는 특히 읽기 전용 함수에서 유용하며, 성능과 안정성을 동시에 잡을 수 있는 실용적인 방식입니다.

💡 TIP: STL에서는 대부분 const 참조를 기본값처럼 사용하는 것이 일반적입니다. 나만의 함수에서도 동일한 습관을 들이면 좋습니다.

이제 함수에 인자를 넘길 때마다 “값으로 줄까? 참조로 줄까?” 고민이 훨씬 덜해지셨을 거예요.
중요한 건 명확한 목적과 코드의 의도를 바탕으로 가장 적절한 방식을 선택하는 것입니다.


자주 묻는 질문 (FAQ)

값 전달과 참조 전달 중 어느 쪽이 더 안전한가요?
일반적으로는 값 전달이 더 안전합니다. 복사본을 넘기기 때문에 함수 내부에서 값이 변경되더라도 원본에는 영향을 주지 않죠. 반면, 참조 전달은 원본을 직접 다루기 때문에 실수로 값을 바꿀 가능성이 있습니다.
참조 전달은 항상 성능이 좋은가요?
대부분의 경우 참조 전달이 복사를 피하기 때문에 성능상 유리하지만, 무조건 더 낫다고는 할 수 없습니다. 함수 내에서 원본을 바꿔선 안 되는 상황이라면 const 참조를 사용하는 것이 좋습니다.
std::vector는 참조로 넘기는 게 정답인가요?
대부분의 경우 std::vector는 크기가 크기 때문에 참조로 넘기는 것이 효율적입니다. 특히 읽기 전용으로 사용할 경우 const std::vector&를 권장합니다.
참조로 넘기면 복사 생성자가 호출되지 않나요?
네, 참조로 넘기면 복사 생성자는 호출되지 않습니다. 복사가 일어나지 않기 때문에 생성자 오버헤드도 줄일 수 있습니다. 이 점이 참조 전달의 큰 장점 중 하나죠.
값 전달이 더 직관적인 이유는 뭔가요?
값 전달은 복사된 값을 넘기기 때문에 함수 내부에서 어떤 일이 일어나도 원본에는 영향이 없습니다. 이로 인해 예측 가능한 코드 작성이 가능하며, 디버깅도 상대적으로 쉽습니다.
const 참조는 왜 그렇게 많이 쓰이나요?
const 참조는 복사를 피하면서도 원본 데이터를 수정할 수 없도록 막아줍니다. 특히 큰 객체나 컨테이너를 읽기만 할 때 매우 유용하며, 성능과 안정성을 동시에 만족시킬 수 있습니다.
복잡한 클래스도 참조 전달이 더 좋은가요?
네, 일반적으로 복사 비용이 큰 복잡한 클래스는 참조 전달이 유리합니다. 단, 원본을 변경할 필요가 없다면 const 참조를 사용하는 것이 더 안전합니다.
참조 대신 포인터를 사용하는 경우도 있나요?
포인터는 null을 전달할 수 있다는 특징이 있어 조건부 참조가 필요한 경우 사용됩니다. 하지만 일반적인 상황에서는 참조가 더 명확하고 안전하게 사용될 수 있습니다.


📌 C++ 함수 인자 전달 방식, 상황에 맞게 선택하세요

이번 글에서는 C++ 함수 호출 시 흔히 사용되는 값 전달(pass by value)참조 전달(pass by reference)의 개념부터 성능 차이, 코드 예제, 객체 전달 시 주의사항까지 상세히 알아보았습니다.
값 전달은 안전하고 직관적이지만 복사 비용이 발생하며, 참조 전달은 효율적이지만 원본이 변경될 수 있다는 위험이 존재합니다.
특히 객체를 다룰 때는 참조 전달을 사용하되, const & 형태를 통해 안정성을 확보하는 것이 좋은 습관입니다.
결국 핵심은 “함수의 목적”과 “데이터의 특성”을 고려하여 상황에 맞는 방식을 선택하는 것입니다.
개발자가 의도를 명확히 표현할 수 있는 코드야말로 유지보수성과 성능을 동시에 만족시키는 최고의 선택입니다.


🏷️ 관련 태그:C++함수전달, 참조전달, 값전달, const참조, 성능최적화, C++객체전달, 메모리관리, C++초보자, 함수인자, C++참조