On this page
article
C++ Operator Overloading
연산자 재정의
연산자 오버로딩은 C++에서 사용자 정의 타입에 대해 연산자의 동작을 재정의하는 기능입니다.
일반 함수로 구현
// 컴파일러가 p1 + p2를 operator+(p1, p2)로 변환
Point operator+(const Point& p1, const Point& p2) {
// 구현부
}
// private 멤버 접근을 위해 friend로 등록
friend Point operator+(const Point& p1, const Point& p2);
멤버 함수로 구현
// 컴파일러가 p1 + p2를 p1.operator+(p2)로 변환
Point operator+(const Point& p2) {
// 구현부
}
연산자 오버로딩 제약사항
- 인자가 모두 기본 타입(primitive)인 경우는 오버로딩 불가
=
,()
,[]
,->
네 가지는 반드시 멤버 함수로만 오버로딩 가능- 새로운 연산자 생성 불가
- 연산자의 인자 개수 변경 불가
- 연산자 우선순위 변경 불가
- 디폴트 파라미터 사용 불가
연산자 오버로딩 예시
1. 스트림 출력 연산자 오버로딩
ostream과 cout
cout
은ostream
타입의 객체<<
는operator<<
로 재정의된 것- C++98 표준화 이후
basic_ostream<>
클래스 템플릿으로 유니코드 출력 가능
사용자 정의 타입 출력
// std::cout << pt 분석
// 컴파일러는 operator<<(cout, pt)를 탐색
std::ostream& operator<<(std::ostream& os, const Point& pt) {
os << "(" << pt.x << ", " << pt.y << ")";
return os;
}
// private 멤버 접근을 위해 friend로 선언
friend std::ostream& operator<<(std::ostream& os, const Point& pt);
endl의 원리
cout
은 객체로.put
,.flush
등 다양한 멤버 함수 사용 가능endl(cout)
==cout << endl
endl
은 함수인데cout.operator<<(함수포인터)
버전이 오버로딩되어 있어서 가능
사용자 정의 조작자
// cout << tab
ostream& tab(ostream& os) {
os << '\t';
return os;
}
2. 증가/감소 연산자 오버로딩
사용 예시
- STL 반복자
전위형과 후위형 구분
++pt; // p.operator++() 호출
pt++; // p.operator++(int) 호출 - 컴파일러는 int 매개변수 있는 함수를 탐색
구현 예시
// 전위형 (++pt)
Point& operator++() {
++x;
++y;
return *this;
}
// 후위형 (pt++)
Point operator++(int) {
Point temp(*this);
++(*this); // 전위형 연산자 재활용
return temp;
}
유의사항:
- 참조로 리턴하지 않을 시
++++pt
에서 두 번째++
가 반환된 임시객체의 값을 증가시키는 문제 발생 - 일반적으로 전위형이 더 효율적(후위형은 복사본 생성)이지만, 최적화될 수 있음
3. 대입 연산자
// p2 = p1 => p2.operator=(p1)
Point& operator=(const Point& p) {
if (this != &p) { // 자기 대입 검사
x = p.x;
y = p.y;
}
return *this;
}
4. 스마트 포인터 연산자
스마트 포인터는 객체지만 포인터처럼 동작하는 객체입니다.
구현 예시
template<class T>
class Ptr {
T* pObj;
public:
Ptr(T* p = 0): pObj(p) {}
T* operator->() {
return pObj;
}
T& operator*() {
return *pObj;
}
};
int main() {
Ptr<Car> p = new Car;
p->Go(); // p.operator->()->Go()
(*p).Go(); // (p.operator*()).Go()
}
장점
- 포인터가 아니라 객체이므로 생성/복사/대입/파괴 모든 과정에 원하는 작업 수행 가능
shared_ptr
- 표준 스마트 포인터 (C++11)
shared_ptr
의 생성자는explicit
이어서:
std::shared_ptr<Car> p1 = new Car; // 오류
std::shared_ptr<Car> p1(new Car); // 정상