[열혈 C++] Chap11: 연산자의 오버로딩2 - 3) 그 이외의 연산자 오버로딩
3. 그 이외의 연산자 오버로딩
- new와 delete도 연산자이기 때문에 오버로딩이 가능하다
- 스마트 포인터와 펑터라는 것도 있다
✏️new 연산자 오버로딩
- 기본적인 new 역할
- 메모리 공간 할당
- 생성자 호출
- 할당하고자 하는 자료형 맞게 반환된 주소 값의 형 변환
- 우리는 이 셋 중 첫번째(메모리 공간의 할당)만 오버로딩 할 수 있다
void * operator new(size_t size) {
void * adr = new char[size];
return adr;
}
- 이렇게 오버로딩 하기로 약속되었다
- 이 상태에서
Point * p = new Point(3,4);
를 만나면 먼저 필요한 메모리 공간을 계산하고 new 함수를 호출하면서 크기를 바이트 단위로 전달한다.
✏️delete 연산자 오버로딩
- 소멸자는 오버로딩 된 함수가 호출되기 전에 호출되므로 오버로딩 된 함수에서 메모리 공간의 소멸을 책임져야 한다
void * operator delete(void * adr) {
delete []adr;
}
-
예제
#include <iostream> using namespace std; class Point { private: int xpos, ypos; public: Point(int x=0, int y=0) : xpos(x), ypos(y) { } friend ostream& operator<<(ostream& os, const Point& pos); static void * operator new (size_t size) // new 오버로딩 { cout<<"operator new : "<<size<<endl; void * adr=new char[size]; return adr; } static void operator delete (void * adr) // delete 오버로딩 { cout<<"operator delete ()"<<endl; delete []adr; } }; ostream& operator<<(ostream& os, const Point& pos) { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } int main(void) { Point * ptr=new Point(3, 4); cout<<*ptr; delete ptr; return 0; }
operator new
와operator delete
함수는 static 함수이기 때문에 아직 객체 생성이 완성되지 않았음에도 멤버함수 호출이 가능한 것이다
✏️operator new[]
-
배열 할당 시 호출되는 함수
-
컴파일러는 객체에 필요한 메모리 공간을 바이트 단위로 계산하여 인자로 전달하면서 이 함수를 호출한다.
-
delete[] 함수도 마찬가지이다. 컴파일러는 소멸자를 호출한 이후에 배열에 저장된 주소값을 전달하면서 이 함수를 호출한다
-
예제
#include <iostream> using namespace std; class Point { private: int xpos, ypos; public: Point(int x=0, int y=0) : xpos(x), ypos(y) { } friend ostream& operator<<(ostream& os, const Point& pos); void * operator new (size_t size) { cout<<"operator new : "<<size<<endl; void * adr=new char[size]; return adr; } void * operator new[] (size_t size) { cout<<"operator new [] : "<<size<<endl; void * adr=new char[size]; return adr; } void operator delete (void * adr) { cout<<"operator delete ()"<<endl; delete []adr; } void operator delete[] (void * adr) { cout<<"operator delete[] ()"<<endl; delete []adr; } }; ostream& operator<<(ostream& os, const Point& pos) { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } int main(void) { Point * ptr=new Point(3, 4); Point * arr=new Point[3]; delete ptr; delete []arr; return 0; } /*결과 oeprator new : 8 operator new [] : 24 operator delete () operator delete[] () */
✏️포인터 연산자 오버로딩
- 대표적 포인터 연산자
-> : 포인터가 가리키는 객체의 멤버에 접근
* : 포인터가 가리키는 객체에 접근#include <iostream> using namespace std; class Number { private: int num; public: Number(int n) : num(n) { } void ShowData() { cout<<num<<endl; } Number * operator->() { return this; // 객체 자신을 참조형태로 반환하도록 오버로딩하고 있다 } Number & operator*() { return *this; } }; int main(void) { Number num(20); num.ShowData(); (*num)=30; //(num.operator*())=30; num->ShowData(); //num.operator->()->ShowData(); (*num).ShowData(); //(num.operator*()).ShowData(); return 0; } /* 20 30 30 */
✏️스마트포인터
- 포인터 역할을 하는 객체로 구현해야할 대상이다
#include <iostream> using namespace std; class Point { private: int xpos, ypos; public: Point(int x=0, int y=0) : xpos(x), ypos(y) { cout<<"Point 객체 생성"<<endl; } ~Point() { cout<<"Point 객체 소멸"<<endl; } void SetPos(int x, int y) { xpos=x; ypos=y; } friend ostream& operator<<(ostream& os, const Point& pos); }; ostream& operator<<(ostream& os, const Point& pos) { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } class SmartPtr { private: Point * posptr; public: SmartPtr(Point * ptr) : posptr(ptr) { } // 생성자에서 동적할당 하지 않음에도 불구하고 소멸자에서 delete연산을 한다 // 생성자의 인자로 전달되는 주소 값은 new연산에 의해 동적할당된 객체의 주소값이다 Point& operator*() const { return *posptr; } Point* operator->() const { return posptr; } ~SmartPtr() { delete posptr; } }; int main(void) { SmartPtr sptr1(new Point(1, 2)); // 포인터 객체를 생성하면서 동시에 스마트 포인터 객체가 이를 가리키게 하고있다 SmartPtr sptr2(new Point(2, 3)); // 이로써 sptr1, sptr2, sptr3는 Point 객체를 가리키는 포인터처럼 동작한다 SmartPtr sptr3(new Point(4, 5)); cout<<*sptr1; cout<<*sptr2; cout<<*sptr3; sptr1->SetPos(10, 20); sptr2->SetPos(30, 40); sptr3->SetPos(50, 60); cout<<*sptr1; cout<<*sptr2; cout<<*sptr3; return 0; }
- Point 객체의 소멸을 위해 delete 연산이 자동으로 이뤄졌다(스마트 포인터의 똑똑함)
✏️펑터
-
함수처럼 동작하는 클래스
#include <iostream> using namespace std; class Point { private: int xpos, ypos; public: Point(int x=0, int y=0) : xpos(x), ypos(y) { } Point operator+(const Point & pos) const { return Point(xpos+pos.xpos, ypos+pos.ypos); } friend ostream& operator<<(ostream& os, const Point& pos); }; ostream& operator<<(ostream& os, const Point& pos) { os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } class Adder { public: int operator()(const int &n1, const int &n2) { return n1+n2; } double operator()(const double &e1, const double &e2) { return e1+e2; } Point operator()(const Point &pos1, const Point &pos2) { return pos1+pos2; } }; int main(void) { Adder adder; cout<<adder(1, 3)<<endl; cout<<adder(1.5, 3.7)<<endl; cout<<adder(Point(3, 4), Point(7, 9)); return 0; }
- Adder클래스와 같이 함수처럼 동작하는 클래스를 펑터라고 하며 함수 오브젝트라고도 불린다.
- 펑터는 함수 똔느 객체의 동작방식에 유연함을 제공할 때 사용된다.