C++ 연산자 오버로딩 마스터하기: 실전 예제와 모범 사례
C++ 연산자 오버로딩
이 튜토리얼에서는 예제와 함께 연산자 오버로딩을 학습합니다.
C++에서는 객체와 구조체 같은 사용자 정의 타입에 대해 연산자 작동 방식을 변경할 수 있습니다. 이를 연산자 오버로딩(operator overloading)이라 합니다. 예를 들어,
Complex 클래스로 표현되는 복소수 객체 c1, c2, result 세 개를 만들었다고 가정합시다.
연산자 오버로딩으로 + 연산자 작동 방식을 재정의하면, 다음과 같이 c1과 c2의 복소수를 더할 수 있습니다:
result = c1 + c2;
다음과 같은 방식 대신 말이죠:
result = c1.addNumbers(c2);
이로써 코드가 직관적이고 이해하기 쉬워집니다.
참고: int, float, char 같은 기본 데이터 타입에는 연산자 오버로딩을 사용할 수 없습니다.
C++ 연산자 오버로딩 문법
연산자를 오버로드하려면 특수한 operator 함수를 사용합니다. 오버로드할 연산자가 작동할 클래스나 구조체 내부에서 함수를 정의합니다.
class className { ... .. ... public returnType operator symbol (arguments) { ... .. ... } ... .. ... };
여기서,
returnType은 함수 반환 타입입니다. operator는 키워드입니다. symbol은 오버로드할 연산자. 예: +, <, -, ++ 등. arguments는 함수에 전달될 인수들입니다.
단항 연산자 오버로딩
단항 연산자는 피연산자 하나만 다룹니다. 증감 연산자 ++와 --가 단항 연산자 예입니다.
예제 1: ++ 연산자(단항) 오버로딩 - 전위
// 전위 ++ 오버로드 #include <iostream> using namespace std; class Count { private: int value; public: // 카운트 5로 초기화 생성자 Count() : value(5) {} // 전위 ++ 오버로드 void operator ++ () { ++value; } void display() { cout << "Count: " << value << endl; } }; int main() { Count count1; // "void operator ++ ()" 호출 ++count1; count1.display(); return 0; }
출력
Count: 6
++count1; 사용 시 void operator ++ ()가 호출돼 객체 count1의 value 속성이 1 증가합니다.
참고: 연산자 오버로딩 시 원하는 대로 동작하게 할 수 있습니다. 예: ++로 value를 100 증가시킬 수도 있습니다.
하지만 이는 코드를 혼란스럽고 이해하기 어렵게 만듭니다. 프로그래머로서 연산자 오버로딩을 적절하고 직관적이며 일관되게 사용하는 건 우리의 책임입니다.
위 예제는 ++가 전위일 때만 작동합니다. 후위로 작동하게 하려면 다음 문법을 씁니다.
void operator ++ (int) { // 코드 }
괄호 안 int에 주목하세요. 단항 연산자를 후위로 쓰기 위한 문법이며, 함수 매개변수가 아닙니다.
예제 2: ++ 연산자(단항) 오버로딩 - 전위/후위 모두
// 전위와 후위 ++ 오버로드 #include <iostream> using namespace std; class Count { private: int value; public: // 카운트 5로 초기화 생성자 Count() : value(5) {} // 전위 ++ 오버로드 void operator ++ () { ++value; } // 후위 ++ 오버로드 void operator ++ (int) { value++; } void display() { cout << "Count: " << value << endl; } }; int main() { Count count1; // "void operator ++ (int)" 호출 count1++; count1.display(); // "void operator ++ ()" 호출 ++count1; count1.display(); return 0; }
출력
Count: 6 Count: 7
예제 2는 ++를 전위와 후위 모두에 쓰게 합니다. 하지만 다음과 같은 시도는 실패합니다:
Count count1, result; // 오류 result = ++count1;
이유는 연산자 함수 반환 타입이 void이기 때문입니다. 연산자 함수 반환 타입을 Count로 만들면 해결됩니다.
// 전위 ++ 시 Count 반환 Count operator ++ () { // 코드 } // 후위 ++ 시 Count 반환 Count operator ++ (int) { // 코드 }
예제 3: 연산자 함수에서 반환값 주기 (++ 연산자)
#include <iostream> using namespace std; class Count { private: int value; public : // 카운트 5로 초기화 생성자 Count() : value(5) {} // 전위 ++ 오버로드 Count operator ++ () { Count temp; // 호출 객체의 value 속성 temp.value = ++value; return temp; } // 후위 ++ 오버로드 Count operator ++ (int) { Count temp; // 호출 객체의 value 속성 temp.value = value++; return temp; } void display() { cout << "Count: " << value << endl; } }; int main() { Count count1, result; // "Count operator ++ ()" 호출 result = ++count1; result.display(); // "Count operator ++ (int)" 호출 result = count1++; result.display(); return 0; }
출력
Count: 6 Count: 6
전위 연산자 오버로딩에 다음 코드 사용:
// 전위 ++ 오버로드 Count operator ++ () { Count temp; // 호출 객체의 value 속성 temp.value = ++value; return temp; }
후위 연산자 오버로딩 코드도 유사합니다. temp 객체 생성해 그 값을 연산자 함수에 반환합니다.
또한 다음 코드 주목:
temp.value = ++value;
변수 value는 main()의 count1 객체 소속입니다. count1이 함수를 호출하기 때문이죠. 반면 temp.value는 temp 객체 소속입니다.
이항 연산자 오버로딩
이항 연산자는 두 피연산자에서 작동합니다. 예:
result = num + 9;
여기서 +는 피연산자 num과 9에서 작동하는 이항 연산자입니다.
사용자 정의 타입에 이항 연산자 오버로드 시:
obj3 = obj1 + obj2;
연산자 함수는 obj1 객체로 호출되고 obj2가 인수로 전달됩니다.
예제 4: C++ 이항 연산자 오버로딩
// 이항 연산자 + 오버로드 C++ 프로그램 // 두 복소수 더하기 #include <iostream> using namespace std; class Complex { private: float real; float imag; public: // real, imag 0으로 초기화 생성자 Complex() : real(0), imag(0) {} void input() { cout << "실수부와 허수부 순서대로 입력: "; cin >> real; cin >> imag; } // + 연산자 오버로드 Complex operator + (const Complex& obj) { Complex temp; temp.real = real + obj.real; temp.imag = imag + obj.imag; return temp; } void output() { if (imag < 0) cout << "출력 복소수: " << real << imag << "i"; else cout << "출력 복소수: " << real << "+" << imag << "i"; } }; int main() { Complex complex1, complex2, result; cout << "첫 번째 복소수 입력:\n"; complex1.input(); cout << "두 번째 복소수 입력:\n"; complex2.input(); // complex1이 연산자 함수 호출 // complex2가 인수로 전달 result = complex1 + complex2; result.output(); return 0; }
출력
첫 번째 복소수 입력: 실수부와 허수부 순서대로 입력: 9 5 두 번째 복소수 입력: 실수부와 허수부 순서대로 입력: 7 6 출력 복소수: 16+11i
이 프로그램에서 연산자 함수는:
Complex operator + (const Complex& obj) { // 코드 }
대신 이렇게 쓸 수도 있습니다:
Complex operator + (Complex obj) { // 코드 }
그러나,
& 사용으로 complex2 객체 참조해 연산자 함수 내 중복 객체 생성 방지, 효율성 향상. const 사용은 complex2 수정을 막아 모범 사례로 간주.

C++ 연산자 오버로딩 시 기억할 사항
=와 & 두 연산자는 C++에서 기본적으로 이미 오버로드되어 있습니다. 예: 같은 클래스 객체 복사 시 = 연산자 직접 사용 가능. 연산자 함수 만들 필요 없음. - 연산자 오버로딩으로 연산자 우선순위와 결합성 변경 불가. 평가 순서 변경 원하면 괄호 사용.
- C++에서 오버로드 불가능한 4개 연산자:
:: (범위 해결) . (멤버 선택) .* (함수 포인터 통한 멤버 선택) ?: (삼항 연산자)
더 배우기:
- 증가 연산자 올바르게 오버로드하는 법?
- 이항 연산자 - 오버로드해 복소수 빼기?