+/+=, =에서 많이 쓰는 정석 시그니처(const/참조/반환 타입)explicit으로 의도치 않은 암시적 변환을 막는 법pos1 + pos2, pos += other가 어떤 함수 호출로 바뀌는지 설명할 수 있다.10 + pos가 왜 어려운지”를 설명할 수 있다.explicit이 없을 때 생길 수 있는 실수를 예시로 설명할 수 있다.연산자 오버로딩은 “새로운 능력”을 부여하는 기능이 아니라,
이미 가능한 일을 더 읽기 쉬운 문법으로 표현하기 위한 장치입니다.
Position a + b (좌표 더하기), a == b (동등 비교)a + b가 “파괴적인 변경”을 일으키거나, 의미가 직관과 다를 때pos1 + pos2의 기본형멤버 함수로 오버로딩하면, 왼쪽 피연산자(pos1)가 곧 this가 됩니다.
class Position {
public:
Position() = default;
Position(int x, int y) : _x(x), _y(y) {}
// 읽기 전용 연산은 const가 거의 정답
Position operator+(const Position& rhs) const
{
return Position(_x + rhs._x, _y + rhs._y);
}
private:
int _x = 0;
int _y = 0;
};
핵심 규칙:
const T&constPosition 리턴)멤버로 만들면 왼쪽이 반드시 Position이어야 합니다.
pos + 10은 가능(오른쪽 변환은 설계에 따라 가능)10 + pos는 왼쪽이 int라서 멤버 함수로는 처리 불가그래서 “양쪽을 대칭으로 처리하고 싶다”면 비멤버(전역) 함수가 자연스럽습니다.
class Position {
public:
Position() = default;
Position(int x, int y) : _x(x), _y(y) {}
Position& operator+=(const Position& rhs)
{
_x += rhs._x;
_y += rhs._y;
return *this;
}
int X() const { return _x; }
int Y() const { return _y; }
private:
int _x = 0;
int _y = 0;
};
// +는 보통 +=를 활용해서 구현(중복 제거)
inline Position operator+(Position lhs, const Position& rhs)
{
lhs += rhs;
return lhs;
}
// 예: int + Position을 지원하고 싶다면(학습용 예시)
inline Position operator+(int a, Position b)
{
return Position(b.X() + a, b.Y() + a);
}
정리:
+= 같은 “자기 자신을 바꾸는 연산”은 멤버 함수가 자연스럽습니다.operator=)의 핵심: “참조 반환”과 “Rule of 0”대부분의 클래스는 직접 operator=를 구현할 필요가 없습니다. (Rule of 0)
그럼에도 규칙 자체는 알아야 합니다.
T&return *this; 덕분에 연쇄 대입이 됩니다: a = b = c;class Position {
public:
Position& operator=(const Position& other) = default;
private:
int _x = 0;
int _y = 0;
};
=처럼 보여도 의미가 다르다Position p = ...;는 “새 객체를 만드는 중”이라 생성/초기화에 가깝습니다.p = ...;는 “이미 존재하는 객체를 바꿈”이라 대입입니다.┌─────────────────────────────────────────────────────────────────────────────┐
│ 같은 = 기호처럼 보여도, 호출되는 함수/의미가 다르다 │
├─────────────────────────────────────────────────────────────────────────────┤
│ 코드 실제 의미(감각) │
│ ───────────────────────────────────────────────────────────────────────── │
│ Position p = Position(1, 2); "p를 만들면서 초기화" │
│ │
│ Position p; "p를 먼저 만든 뒤" │
│ p = Position(1, 2); "이미 있는 p에 대입" │
└─────────────────────────────────────────────────────────────────────────────┘
explicit: 의도치 않은 암시적 변환을 차단하는 안전장치인자가 1개인 생성자는 “변환 생성자”가 될 수 있습니다.
explicit이 없으면, 컴파일러가 여러분 대신 조용히 변환을 시도합니다.
class Position1D {
public:
explicit Position1D(int v) : _v(v) {}
private:
int _v = 0;
};
void Take(Position1D p) {}
int main()
{
// Take(10); // explicit이라면 여기서 막힘(의도치 않은 변환 방지)
}
실전 감각:
explicit부터 붙인다고 생각해도 좋습니다.10 + pos를 지원하려면 “멤버 함수”와 “비멤버 함수” 중 어느 쪽이 더 적합할까?operator+에 const가 붙는 이유는?operator+=가 T&를 반환하고 return *this;를 하는 이유는?explicit이 없으면 어떤 형태의 버그/실수가 생길 수 있을까?