연산자 오버로딩에 대해 알아 보았다, 하지만 아직 연산자 오버로딩에 대해 완벽하게 알고 있는 것 같진 않다.
void main()
{
Point p1(1, 2);
// p1.operator(10);
p1 + 10;
p1.ShowPoint();
// 10.operator(p1);
10 + p1;
// 과연 이게 우리가 원하는 오버로딩인가?
p1.ShowPoint();
// 값 또한 바뀌지 않았다.
}
// 결과
// 11 12
// 11 12
그렇다면 저 구문에 대해서 실행을 해주고 싶다면 어떻게 해야 할까?
이런 부분에 대해서는 전역으로 연산자 오버로딩을 해 주어야 한다.
class Point
{
...
public:
friend void operator+( int val, Point& p );
}
void operator+( int val, Point& p )
{
// 단순히 자리만 변경하여 사용해 주면 된다.
p+val;
}
void main()
{
Point p1(1, 2);
// p1.operator(10);
p1 + 10;
p1.ShowPoint();
// 10.operator(p1);
10 + p1;
// 이제는 사용이 가능하게 바뀌었다.
p1.ShowPoint();
// 값의 변경이 일어 난 것을 확인 할 수 있다.
}
// 결과
// 11 12
// 21 22
다음은 [] 연산자에 대해서 연산자 오버로딩을 해보자.
#include <iostream>
class AAA
{
int idx;
int arr[5];
public:
AAA():idx(0){}
void AddArr( int n )
{
arr[idx++] = n;
}
void ShowAllData()
{
for( int i=0; i<idx; i++ )
std::cout << arr[i] << std::endl;
}
// [] 연산자를 오버로딩 하여 값을 받고 있다.
int& operator[](int i)
{
idx++;
// arr[i]를 리턴하여 그 값에 대해 숫자를 삽입한다고 볼 수 있다.
return arr[i];
}
};
void main()
{
AAA a;
// a.operarot[](0) = 1;
a[0] = 1;
// a.operarot[](1) = 2;
a[1] = 2;
a[2] = 3;
a[3] = 4;
a[4] = 5;
//a.AddArr(1);
//a.AddArr(2);
//a.AddArr(3);
//a.AddArr(4);
//a.AddArr(5);
a.ShowAllData();
}
// 결과
// 1
// 2
// 3
// 4
// 5
비슷한 내용이지만 이번엔 int가 아닌 객체를 직접 저장하는 배열이 존재한다고 하고,
조금 소스를 변경해 보자.
#include <iostream>
// 배열에 들어 가게될 AAA 클래스
class AAA
{
int x;
int y;
public:
// 디폴트 매개변수를 사용해 AAAArr에서 배열 선언이 가능하게 변경
AAA( int _x=0, int _y=0 ) : x(_x), y(_y){}
// 원활한 출력을 위해 << 연산자를 오버로딩 하였다.
friend std::ostream& operator <<(std::ostream& os, const AAA& p );
};
// 화면에 출력을 맞게 된다.
std::ostream& operator <<(std::ostream& os, const AAA& p )
{
os << "[" << p.x << ", " << p.y << "]";
return os;
}
class AAAArr
{
int idx;
// AAA 객체를 담을 객체 배열
AAA arr[5];
public:
AAAArr() : idx(0){}
void ShowAllData()
{
for( int i=0; i<idx; i++ )
std::cout << arr[i] << std::endl;
}
// [] 연산자를 오버로딩 하여 값을 받고 있다.
AAA& operator[](int i)
{
idx++;
// arr[i]를 리턴하여 그 값에 대해 숫자를 삽입한다고 볼 수 있다.
return arr[i];
}
};
void main()
{
AAAArr a;
// 임시 객체를 선언해 값을 삽입
a[0] = AAA(10, 20);
a[1] = AAA(11, 21);
a[2] = AAA(12, 22);
a[3] = AAA(13, 23);
a[4] = AAA(14, 24);
// 화면에 출력한다.
a.ShowAllData();
}
// 결과
// [10, 20]
// [11, 21]
// [12, 22]
// [13, 23]
// [14, 24]
조금 길게 느껴질 수도 있겠지만 큰 틀은 비슷하다.
단지 객체를 담기 위한 클래스와,
배열 안에 들어갈 클래스를 나눈것 뿐이다.
스스로가 스스로를 담기 위해선,
*를 통해서 해야 될 것으로 생각된다.
다음은 대입 연산자(=)를 오버로딩 하는 것에 대해서 살펴 보자.
실제로 연산자 오버로딩이 이루어진 곳에,
p1=p2;
를 하게 되면 p2에 값이 p1으로 이동 된 것을 알 수 있는데,
실제로 이것은 디폴트 대입 연산자 오버로딩? 정도로 표현 할 수 있을 것이다.
#include <iostream>
class Point
{
int x;
int y;
public:
Point( int _x=0, int _y=0 ) : x(_x), y(_y){}
Point& operator+( const Point& p )
{
x = x+p.x;
y = y+p.y;
return *this;
}
// 선언해 주지 않아도 자동으로 생성되는 디폴트 대입 연산자
//Point& operator=( const Point& p )
//{
// x = p.x;
// y = p.y;
// return *this;
//}
void ShowData()
{
std::cout << x << " " << y << std::endl;
}
};
void main()
{
Point a(1, 1);
Point b(10,10);
a+b;
a.ShowData();
b.ShowData();
// 디폴트 대입 연산자가 발동하였다.
a = b;
// 값의 변경이 일어 난 것이 확인 가능.
a.ShowData();
}
// 결과
// 11 11
// 10 10
// 10 10
어찌 보면 복사 생성자와 같은 맥락이지만,
당연히 기본적인 디폴트 대입 연산자는 얕은 복사가 이루어 진다.
무슨 의미인지 알겠는가?
완벽하게 해주기 위해선,
깊은 복사가 이루어 져야 한다는 것이다.
#include <iostream>
#include <string.h>
class Point
{
char* name;
public:
Point( char* _name )
{
name = new char[strlen(_name)+1];
strcpy( name, _name );
}
~Point()
{
delete name;
}
// 선언해 주지 않아도 자동으로 생성되는 디폴트 대입 연산자
Point& operator=( const Point& p )
{
// 얕은 복사가 이루어 지면 안되기 때문에
// 깊은 복사가 이루어지게 변경해 주어야 한다.
name = new char[strlen(p.name)+1];
strcpy( name, p.name );
return *this;
}
void ShowName()
{
std::cout << name << std::endl;
}
};
void main()
{
Point a("nameA");
Point b("nameB");
a.ShowName();
b.ShowName();
a = b;
a.ShowName();
}
// 결과
// nameA
// nameB
// nameB
'프로그래밍 > C++' 카테고리의 다른 글
템플릿(template) (0) | 2010.04.04 |
---|---|
임시 객체 생성 (0) | 2010.04.04 |
연산자 오버로딩(operator overloading)(1) (0) | 2010.04.04 |
가상 복사 생성자 (0) | 2010.04.03 |
virtual(가상) (0) | 2010.04.03 |