본문 바로가기

STUDY/C++

편리하지만 주의해야 하는 연산자 오버로딩 operator

이번엔 연산자 오버로딩에 대해 포스팅을 해보려고 합니다.

연산자 오버로딩은 기존에 있던 연산자를 정의하는 것입니다. 

 

#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 오버로딩에 대해 알아보았습니다!