다형성(Polymorphism): 연산자 오버로딩
연산자 오버로딩: 객체 지향 프로그래밍의 유연성
객체 지향 프로그래밍 (OOP)에서 다형성 (Polymorphism)은 핵심 개념 중 하나이다. 다형성은 동일한 이름의 함수가 객체의 유형에 따라 다르게 동작하는 것을 의미한다. 오버로딩 (Overloading)은 다형성을 구현하는 방법 중 하나로, 동일한 이름의 함수에 여러 개의 정의를 부여하여 객체의 유형에 따라 적절한 함수가 호출되도록 한다.
C++에서는 연산자 오버로딩을 통해 연산자 (+, -, *, / 등)에 새로운 기능을 추가할 수 있다. 이는 사용자 정의 데이터 유형에 대해 기본 연산자를 사용할 수 있도록 하여 코드의 가독성과 직관성을 향상시킨다.
연산자 오버로딩 방법
C++에서 연산자 오버로딩은 다음 두 가지 방법으로 수행할 수 있다.
- Non-member 함수 접근:
return_type operator@(a, b);
- 멤버 함수 접근:
return_type a.operator@(b);
첫 번째 방법은 연산자 함수를 클래스 외부에서 정의하는 방식이고, 두 번째 방법은 클래스 내부에서 멤버 함수로 정의하는 방식이다. 두 방법의 주요 차이점은 연산자 함수가 클래스의 private 멤버에 접근할 수 있는지 여부이다. 멤버 함수는 클래스의 private 멤버에 접근할 수 있지만, non-member 함수는 접근할 수 없다(그래서 friend 이용).
연산자 오버로딩 예시: <<
연산자
<<
연산자는 일반적으로 출력 스트림에 데이터를 삽입하는 데 사용된다. Complex
클래스를 정의하고 <<
연산자를 오버로딩하여 복소수를 출력 스트림에 삽입할 수 있도록 해 보자.
class Complex {
int* m_r; // 실수 부분
int* m_i; // 허수 부분
public:
friend ostream& operator<<(ostream& o, Complex c);
};
ostream& operator<<(ostream& o, Complex c) {
o << *c.m_r << (*c.m_i < 0 ? "" : "+") << *c.m_i << "j";
return o;
}
위 코드에서 operator<<
함수는 Complex
객체를 인자로 받아, 실수 부분과 허수 부분을 출력 스트림에 삽입한다.
![]() |
example code |
연산자 오버로딩 예시: []
연산자
[]
연산자는 배열의 요소에 접근하는 데 사용된다. Vector
클래스를 정의하고 []
연산자를 오버로딩하여 동적 배열의 요소에 접근할 수 있도록 해 보자.
class Vector {
int* m_data; // 동적 배열에 대한 포인터
int m_size; // 배열 크기
public:
int& operator[](int index);
};
int& Vector::operator[](int index) {
if (index >= m_size) {
cout << "Error: index out of bound";
exit(0);
}
return m_data[index];
}
위 코드에서 operator[]
함수는 인덱스를 인자로 받아, 해당 인덱스의 배열 요소를 반환한다.
![]() |
example code(default value is 0) |
L-value와 R-value
L-value는 메모리 위치를 나타내는 값이고, R-value는 메모리 위치에 저장된 값 자체를 나타내는 값이다. L-value는 대입 연산자의 왼쪽과 오른쪽 모두에 나타날 수 있지만, R-value는 오른쪽에만 나타날 수 있다.
![]() |
L-value vs R-value |
연산자 오버로딩 예시: =
연산자
=
연산자는 변수에 값을 대입하는 데 사용된다. Complex
클래스를 정의하고 =
연산자를 오버로딩하여 복소수 객체를 다른 복소수 객체에 대입할 수 있도록 해 보자.
class Complex {
int* m_r; // 실수 부분
int* m_i; // 허수 부분
public:
Complex& operator=(const Complex& c);
};
Complex& Complex::operator=(const Complex& c) {
if (this == &c) {
return *this;
}
delete m_r;
delete m_i;
m_r = new int(*c.m_r);
m_i = new int(*c.m_i);
return * this;
}
위 코드에서 operator=
함수는 Complex
객체를 인자로 받아, 현재 객체의 멤버 변수 값을 인자로 받은 객체의 멤버 변수 값으로 변경한다.
![]() |
example code |
마치며
이번 포스팅에서는 연산자 오버로딩, L-value와 R-value에 대해 알아보았다. 연산자 오버로딩은 객체 지향 프로그래밍의 유연성을 향상시키는 데 중요한 역할을 한다. 다음 시간에는 Inheritance(상속)에 대해 알아보도록 하겠다.