C++ operator
연산자 오버로딩
객체에 대해+
, -
등의 연산을 수행할 때가 있다.
객체끼리 더하거나, 파일에 객체를 스트림으로 전달하거나, 가져올 수도 있다.
클래스에서 덧셈을 구현하는 방법은 크게 세가지 방법이 있을 것이다.
두개를 먼제 비교하자면, 하나는 클래스 내부에 메서드로 구현하는 방법과 다른 하나는 연산자 오버로딩을 통해서 구현할 수 있다.
메서드로 구현하자면
#include <iostream>
template <typename T> class MyClass
{
private:
T _data;
public:
MyClass(T data) : _data(data)
{
}
T getMyData() const
{
return _data;
}
MyClass add(const MyClass &object) const
{
return MyClass<T>(getMyData() + object.getMyData());
}
};
int main(int argc, char *argv[])
{
MyClass<int> first{1};
MyClass<int> second{2};
MyClass<int> third = first.add(second);
return 0;
}
#include <iostream>
template <typename T> class MyClass
{
private:
T _data;
public:
MyClass(T data) : _data(data)
{
}
T getData() const
{
return _data;
}
MyClass operator+(const MyClass &object) const
{
return MyClass(getData() + object.getData());
}
};
int main(int argc, char *argv[])
{
MyClass<int> first{1};
MyClass<int> second{5};
MyClass<int> third = first + second;
return 0;
}
// ...
MyClass<int> third = first + second;
// ...
// ...
MyClass<int> third = first.operator+(second);
// ...
여기서 operator+ 매개변수가 반드시 이 메서드가 속한 클래스와 같은 타입의 객채만 받을 필요는 없다. 해당 객체를 받아서 사용하도록 하면 된다. 그리고 operator+ 의 리턴 타입도 마음껏 정할 수 있다. 함수 오버로딩을 떠올려보면 함수의 리턴 타입을 따지지 않았다. 연산자 오버로딩도 일종의 함수 오버로딩이다.
MyClass first{1}, second{2};
std::string str = "hello";
MyClass third = first + second;
third = first + 0.5;
third = second + 10;
이렇게 묵시적 변환을 활용하면 편리할 때가 있다. 하지만 string_view를 더하는 것은 상식적으로 맞지가 않다. 묵시적으로 변환하지 않으려면 explicit 키워드를 추가한다.
class MyClass
{
public:
MyClass() = default;
MyClass(double data);
explicit MyClass(std::string_view data);
};
- explicit 키워드는 클래스를 정의하는 코드에서만 지정할 수 있다.
- 인수를 하나만 지정해서호출할 수 있는 생성자에만 적합하다. 매개변수가 하나뿐인 생성자나 매개변수를 여러 개 받더라도 디폴드값이 지정된 생성자에만 붙일 수 있다.
- 묵시적 변환을 위해 성성자를 선택하는 과정에서 성능이 떨어질 수 있다. 항상 임시 객체를 생성하기 때문이다. double 값을 더할 때 이렇게 임시 객체가 생성되지 않게 하려면 추가적으로 operator+를 함꼐 정의한다.
전역 함수로 구현하기
세번째 방법으로, 전역 함수로 구현하는 방법이 있다. 지금까지 좌변에 operator를 오버딩한 객체만 두고 코드를 봤는데 이유는 C++가 교환법칙이 성립되지 않기 떄문이다. 왜냐면 operator는 반드시 객체에 대해서 호출해야 하고 이를 위해 객체가 항상 operator의 좌변에 위치해야 하기 때문이다. 하지만 전역 함수로 만들면 특정 객체에 종속되지 않기 때문에 가능하게 할 수 있다.#include <iostream>
class MyClass
{
private:
double _data;
public:
MyClass() = default;
MyClass(double data): _data(data) {};
double getData() const {return _data; }
void printData() {std::cout << "data: " << _data << std::endl; }
};
MyClass operator+(const MyClass& first, const MyClass& second) {
return MyClass(first.getData() + second.getData());
}
int main(int argc, char *argv[])
{
MyClass first{1}, second{2};
MyClass third = first + 3;
third.printData();
third = 4 + 3.0;
third.printData();
return 0;
}
third = 4 + 3;
third = 7;
explicit MyClass(double data): _data(data) {};

참고
- example
- 전문가를 위한 C++