이번엔 연산자 오버로딩에 대해 포스팅을 해보려고 합니다.
연산자 오버로딩은 기존에 있던 연산자를 정의하는 것입니다.
#include <iostream>
using namespace std;
class OperatorTest
{
private:
int _num;
public:
OperatorTest(int num1) : _num(num1) { }
void printMember()
{
cout << "member:" << _num << endl;
}
};
int main()
{
OperatorTest test1(10);
OperatorTest test2(20);
OperatorTest test3 = test1 + test2;
}
operator 가 없는 클래스가 있습니다.
operator 가 없는 클래스는 연산자를 쓰면 오류가 납니다.
오류를 보면 "이러한 피연산자와 일치하는 + 연산자가 없습니다." 라는 에러가 뜨게 됩니다.
연산자 오버로딩을 정의해줍니다.
class OperatorTest
{
private:
int _num;
public:
OperatorTest(int num1) : _num(num1) { }
void printMember()
{
cout << "member:" << _num << endl;
}
OperatorTest operator+(OperatorTest &ref)
{
return OperatorTest(this->_num + ref._num);
}
};
operator+ 가 정의된 후에는 더이상 오류가 나지 않게 됩니다.
이 operator를 보면 안에서 임시객체가 만들어 진 후 반환됩니다.
연산자 operator+는 이런식으로 해석됩니다.
test3 = test1.operator+(test2);
nb1 + nb2 = nb1.operator+(test2);
연산자 오버로딩을 사용하면서 주의할 점은 대입연산자 operator=를 작성시에는 꼭 자기자신에 대한 처리를 해 줘야한다는 점입니다.
예시를 들어볼게요.
class Member
{
public:
int _m;
public:
Member(int m)
{
this->_m = m;
}
};
class OperatorTest
{
private:
int _num;
Member* _member;
public:
OperatorTest(int num1) : _num(num1)
{
_member = new Member(_num);
}
void printMember()
{
cout << "member:" << _member->_m << endl;
}
OperatorTest& operator=(const OperatorTest& whs)
{
delete(_member);
this->_member = whs._member;
return *this;
}
};
위의 코드는 언뜻 보면 정상적으로 보입니다.
하지만 실제 사용시에 아래와 같이 사용하면 결과는 어떻게 될까요 ?
int main()
{
OperatorTest test1(10);
OperatorTest test2(20);
test1.printMember();
test2.printMember();
test1 = test2;
test2 = test2;
test1.printMember();
test2.printMember();
}
[결과]
member:10
member:20
member:-572662307
member:-572662307
member:10
member:20
member:-572662307
member:-572662307
결과는 위와 같이 member 1과 2가 모두 쓰레기값을 가지게 됩니다.
첫 대입연산자 사용에서
test1 = test2;
이 코드는 아래와 같이 해석됩니다.
test1 = test1.operator=(test2);
operator=에 들어간 test1의 멤버 _member는 메모리가 해제되고, test2의 _member변수는 test1의 _member로 복사가 되겠죠. 여기서는 얕은 복사가 될 것입니다.
따라서 test1과 test2는 모두 같은 _member의 메모리를 참조하고 있습니다.
이후에 member2에 자기 자신을 대입하여 operator=가 발생했을 때,
자기 자신의 _member메모리를 해제한 후, 그 해제된 메모리를 다시 자기 자신에게 넣으니 문제가 발생한 것입니다.
_member는 해제된 메모리를 가지고 있는 셈이죠.
test1는 test2와 같은 메모리공간을 바라보고 있으니 당연히 댕글링이 난 것이구요.
이와 같이 자기 자신을 대입하는 상황을 항상 생각하며 operator 오버로딩을 해야합니다.
OperatorTest& operator=(const OperatorTest& whs)
{
if (&whs == this)
{
return *this;
}
delete(_member);
this->_member = whs._member;
return *this;
}
위와 같이 자기자신에 대한 처리를 해 준다면 더이상 이런 문제는 발생하지 않을 것입니다.
오늘은 편리하지만 주의해야 하는 operator 오버로딩에 대해 알아보았습니다!
'STUDY > C++' 카테고리의 다른 글
스마트포인터 shared_ptr 구현하기 (2) - thread safety (0) | 2020.05.30 |
---|---|
스마트포인터 shared_ptr 구현하기 (1) (3) | 2020.05.11 |
std::map에 관한 고찰 (0) | 2020.04.16 |
선언과 정의에 따른 메모리 (0) | 2019.06.23 |
new 와 new []의 차이점 (0) | 2019.06.10 |