프로그래밍/C++

연산자 오버로딩

hscho00 2020. 6. 10. 18:34

Vector result = vector1 + vector2;

Vector result = vector1.operator+(vector2);

반환형 함수이름 인자

 

Vector operator+(const Vector& rhs) const;

형식 손에 익을 정도로 술술 쓸 줄 알아야

마지막에 const는 자신의 값은 변하지 않으므로


* 멤버 연산자 작성하는 법

<return-type><class-name>::operator<operator-symbol>(<argument-list>)

{
}

Vector Vector::operator-(const Vector& rhs) const;

Vector Vector::operator*(const Vector& rhs) const;

Vector Vector::operator/(const Vector& rhs) const;


* friend 키워드

클래스 정의 안에 friend 키워드를 사용 가능

> 다른 클래스나 함수가 나의 private 또는 protected 멤버에 접근할 수 있게 허용

(friend는 내 금고에 있는 거 다 털어가도 돼~)


* 연산자 오버로딩과 const

멤버 변수의 값이 바뀌는 것을 방지

불필요한 개체의 사본이 생기는 것을 방지

최대한 많은 곳에 const 붙일 것!


* 제한사항

1. 오버로딩된 연산자는 최소한 하나의 사용자 정의 형을 가져야 함

Vector operator+(const Vector& rhs) const;

안 그러면 이미 존재하는 연산자일 수도

 

2. 오버로딩된 연산자는 피연산자 수를 동일하게 유지해야 함

Vector vector1;

+vector1;    // 불가능. +연산자는 2개의 피연산자를 가짐

그러나 단항 +연산자를 오버로딩 할 순 있음

 

3. 새로운 연산자 부호를 만들 수 없음

Vector operator@(const Vector& rhs) const;    // 불가능

 

4. 오버로딩 할 수 없는 연산자가 존재

. .* :: ?: 


* 연산자 오버로딩을 남용하지 말자

Vector vector = vector1 << vector2;    // ? 뭐 하고 싶은거야

 

차라리 함수를 이쁘게 만들자


* 대입(assignment)연산자 operator=

복사 생성자와 거의 동일.

차이 1. 따끈따끈하게 갓 만들어진 개체냐(복사 생성자) 이미 있는 개체냐(대입 연산자)

2. 대입 연산자는 메모리를 해제해 줄 필요가 있을 수도...

 

복사 생성자를 구현했다면 대입 연산자도 구현해야 할 것임


* 암시적 operator=

operator= 구현이 안 되어 있으면 컴파일러가 operator= 연산자를 자동으로 만들어 줌 (얕은 복사)

 

 

* 암시적 함수들(매개변수 없는 생성자, 복사 생성자, 소멸자, 대입(=) 연산자) 을 "지우는" 법 (고대 C++ 버전)

Vector() {}

Vector(const Vector& other) {}

~Vector() {}

Vector& operator=(const Vector& rhs);

 

private 멤버로 넣어버리자


* Vector.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#pragma once
#include <iostream>
 
 
class Vector
{
 
public:
    Vector();
    Vector(int x, int y);
    ~Vector();
 
    Vector operator+(const Vector& rhs) const;
 
    // friend : 다른 클래스나 함수가 나의 private 또는 protected 멤버에 접근할 수 있게 허용
    // friend <return-type> operator<operator-symbol>(<argument-list>);
    // firend 함수는 멤버 함수가 아님!!!
    friend std::ostream& operator<<(std::ostream& os, const Vector& rhs);
 
    // 좌항의 값이 변하길 기대하므로 마지막에 const 사용 X
    // 개체 복사를 막기위해 Vector&
    Vector& operator+=(const Vector& rhs);
 
    bool operator==(const Vector& rhs) const;
 
    Vector operator*(const Vector& rhs) const;
    Vector operator*(int multiplier) const;
    friend Vector operator*(int multiplier, const Vector& v);    // 좌항이 int일 경우도 똑같이 동작하도록 
                                                                //전역 함수 만들고 매개변수 2개
 
    Vector& operator*=(const Vector& rhs);
    Vector& operator*=(int multiplier);
 
private:
    int mX;
    int mY;
 
};
cs

* Vector.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "Vector.h"
 
 
Vector::Vector()
    : mX(0), mY(0)
{
 
}
 
 
Vector::Vector(int x, int y)
    : mX(x), mY(y)
{
 
}
 
 
Vector::~Vector()
{
 
}
 
 
Vector Vector::operator+(const Vector& rhs) const
{
    Vector sum;
    sum.mX = mX + rhs.mX;
    sum.mY = mY + rhs.mY;
 
    return sum;
}
 
 
// <return-type> operator<operator-symbol>(<argument-list>)
// 전역 함수는 어디에 써도 상관은 없지만, 같이 쓰이는 클래스의 cpp에 넣어 놓는게 일반적
// 반환값이 void면 Chaining 불가능
// os에 뭔가 쓰니까 const 붙이지 않았음. 반환형도 마찬가지(내가 쓰던거 반환해 줄테니까 너도 마음대로 써~)
std::ostream& operator<<(std::ostream& os, const Vector& rhs)
{
    //Vector 클래스에서 friend 선언을 했으므로 private 멤버 변수 접근 가능
    os << rhs.mX << ", " << rhs.mY;
 
    return os;
}
 
 
Vector& Vector::operator+=(const Vector& rhs)
{
    mX += rhs.mX;
    mY += rhs.mY;
 
    return *this;    // this : 나 자신을 가리키는 포인터
                    // *this : 나(개체), 반환하면 참조로 받아준다.
}
 
 
bool Vector::operator==(const Vector& rhs) const
{
    return (mX == rhs.mX && mY == rhs.mY);
}
 
Vector Vector::operator*(const Vector& rhs) const
{
    Vector result(mX * rhs.mX, mY * rhs.mY);
 
    return result;
}
 
Vector Vector::operator*(int multiplier) const
{
    Vector result(mX * multiplier, mY * multiplier);
 
    return result;
}
 
Vector operator*(int multiplier, const Vector& v)
{
    Vector result(v.mX * multiplier, v.mY * multiplier);
 
    return result;
}
 
Vector& Vector::operator*=(const Vector& rhs)
{
    mX *= rhs.mX;
    mY *= rhs.mY;
 
    return *this;
}
 
Vector& Vector::operator*=(int multiplier)
{
    mX *= multiplier;
    mY *= multiplier;
 
    return *this;
}
cs

* main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include "Vector.h"
 
 
int main()
{
    Vector v1(1020);
    Vector v2(317);
 
 
    Vector sum = v1 + v2;    // + 연산자 오버로딩
    sum = v1.operator+(v2);    // 완전히 같은 의미
 
 
    //하고 싶은 짓
    //std::cout << v1;        // 10, 20 콘솔창에 뜨기 바람
 
    //private 멤버 변수 접근 불가
    //std::cout << v1.mX << ", " << v1.mY << std::endl;
 
    //첫 번째 시도 : Getter 만들기
    //모든 멤버 변수에 Getter 만드는 것도 좀...
    //std::cout << v1.GetX() << ", " << v1.GetY() << std::endl;
 
    //두 번째 시도 : << 연산자 오버로딩
    //std::cout.operator<<(v1);    // cout이 어디에 들어있지?
                                // ostream 클래스에 넣으면 되지 않나?
                                // 남이 만든 파일에 손대는것 자체가 말이 안돼...
                                // 좌항에 해당되는 개체에 접근 권한이 없을 때,
                                // 전역 함수를 만들어야 함
 
    std::cout << v1 << std::endl;    // << 반환형이 void 였다면 불가능
 
    
    v1 += v2;
    std::cout << v1 << std::endl;
 
    std::cout << (v1 == v2) << std::endl;
 
    std::cout << (v1 * v2) << std::endl;
    std::cout << (v1 * 3<< std::endl;
    std::cout << (3 * v1) << std::endl;
 
    v1 *= v2;
    std::cout << v1 << std::endl;
 
    v1 *= 3;
    std::cout << v1 << std::endl;
 
    return 0;
}
cs

실행 결과

 

 

출처 : 포큐아카데미 C++ 언매니지드 프로그래밍