[문과 코린이의 IT 기록장] C,C++ - 연산자 오버로딩 6 : new & delete 연산자 오버로딩 (new 연산자 오버로딩에 대한 상세한 이해, operator new & operator new[ ])
[문과 코린이의 IT 기록장] C,C++ - 연산자 오버로딩 6 : new & delete 연산자 오버로딩
(new 연산자 오버로딩에 대한 상세한 이해, operator new & operator new[ ])
1. new 연산자 오버로딩에 대한 상세한 이해
- new & delete 연산자 오버로딩은, 기존의 앞서 봤던 연산자 오버로딩과 많이 다르다.
* new & delete 또한, 연산자이다. 따라서 연산자 오버로딩이 가능하다.
1) new 연산자가 하는 일
a. 메모리 공간의 할당
b. 생성자의 호출
c. 할당하고자 하는 자료형에 맞게 반환된, 주소 값의 형 변환
- 이와 같이, 객체의 생성과정은 다소 복잡한 과정을 거친다.
- 그러나 이 중, 생성자의 호출과 반환된 주소값의 형변환은 컴파일러가 진행한다.
- 즉, new 연산자를 오버로딩할 때는 메모리 공간의 할당만 책임을 지면 된다. 다시말하자면, new 연산자가 진행하는 세 가지 작업 중, 1번에 해당하는 메모리 공간의 할당만 오버로딩 할 수 있다는 것이다.
2) new 연산자 오버로딩 방법
void *operator new(size_t size) {....}
- 반환형은 반드시 void형
- 매개변수형은 size_t형
- 오버로딩 된 함수는 컴파일러에 의해 호출이 이루어지며, 해당 클래스를 대상으로 new 연산자가 오버로딩됨.
3) new 연산자 오버로딩
[ new & delete 연산자를 오버로딩할 대상 클래스 ]
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); }; ostream& operator<<(ostream &os, const Point &pos){ os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; return os; } |
[ new 연산문 ]
Point *ptr = new Point(3,4); // 힙공간 X, 메모리 공간에 할당한다.
- 먼저 필요한 메모리 공간을 계산한다. 그 크기가 계산되면, operator new 함수를 호출하면서, 계산된 크기의 값을 인자로 전달한다. 크기정보는 바이트(byte)단위로 계산되어 전달된다.
- 따라서, 아래의 형태로 operator new 함수를 정의해야 한다.
void *operator new(size_t size){ |
- 즉, 이와 같이 (a) operator new함수가 할당한 메모리 공간의 주소 값을 반환하면, 컴파일러는 (b) 생성자를 호출해서 메모리 공간을 대상으로 초기화를 진행한다. 그리고 (c) 완성된 객체의 주소 값을 Point 클래스의 포인터 형으로 반환한다.
// new 연산자가 반환하는 값은, operator new 함수가 반환하는 값이 아니다. operator new함수가 반환하는 값은, 컴파일러에 의해 적절히 형 변환된 값이며, 생성자의 호출정보는 operator new함수와 아무런 관련이 없다.
4) delete 연산자 오버로딩
Point *ptr = new Point(3,4); // 이와 같이 객체 생성 이후에,
delete ptr; // 이와같이 객체의 소멸을 명령할 수 있다.
- 컴파일러는 먼저 ptr이 가리키는 객체의 소멸자를 호출한다.
void opeerator delete(void *adr){ // 이 함수에 ptr에 저장된 주소값을 전달함.
delete [ ] adr; // 메모리 공간의 소멸을 책임짐.
/* 사용하는 컴파일러에서, void형 대상의 delete 연산을 허용하지 않는다면?
delete [ ] ((char*) adr);
- char형으로 변환해서 delete 연산을 진행하기.
*/
}
5) new & delete 연산자 오버로딩 예제
ex )
#include <iostream>
using namespace std;
class Point {
private:
int xpos, ypos;
public:
Point(int x = 0, int y = 0) :xpos(x), ypos(y) {} // Point 클래스의 생성자
friend ostream& operator<< (ostream & os, const Point & pos);
void* operator new(size_t size) { // operator new 함수
cout << "operator new : " << size << endl;
void* adr = new char[size]; // byte 단위로 필요한 메모리 공간을 할당하고 있음. 할당에 사용되는 크기정보는 컴파일러가 계산해서 전달해줌.
return adr;
}
void operator delete(void* adr) { // operator delete 함수
cout << "operator delete()" << endl;
delete[]adr; // 배열의 형태로 메모리 공간을 할당했기 때문에, 배열의 삭제를 위한 delete문 구성 (char형으로 해제)
}
};
ostream& operator<<(ostream& os, const Point& pos) { // 객체의 값 출력을 위해 만들어진, cout 연산자 오버로딩
os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;
return os;
}
int main() {
Point* ptr = new Point(3, 4); // operator new 함수 호출 부분
// 아직 객체생성이 완성된 상태가 아닌데, 맴버함수의 호출이 가능한 이유는 ?
: operator new함수와, operator delete 함수가 static으로 선언된 함수이기 때문이다.
cout << *ptr;
delete ptr; // operator delete 함수 호출 부분
return 0;
}
2. operator new & operator new[ ]
1) new 연산자의 오버로딩
- new 연산자는 두 가지 형태로 오버로딩이 가능하다.
a. void *operator new(size_t size) { .... } // 앞에 설명했던 내용과 같음
b. void *operator new[ ](size_t size) { .... } // new 연산자를 이용한 배열할당 시 호출되는 함수.
ex ) Point *arr = new Point[3];
// 3개의 Point 객체에 필요한 메모리 공간을 byte 단위로 계산해서, 이를 인자로 전달하면서 void *operator new[ ] (size_t size){ .... } 함수를 호출한다.
2) delete 연산자의 오버로딩
- delete 연산자 또한 두가지 형태로 오버로딩이 가능하다.
a. void operator delete(void *adr) { .... } // 위에 설명한 내용과 같음
b. void operator delete[ ](void *adr) { .... } // delete 연산자를 이용한 배열소멸 시 호출되는 함수
ex ) delete [ ] arr;
// 컴파일러는 소멸자를 호출한 이후에, arr에 저장된 주소값을 전달하면서 void operator delete[ ](void * adr){ .... }함수를 호출한다.
3) operator new [ ] & operator 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); // 객체 출력을 위한, ostream & operator<<함수
void* operator new(size_t size){ // new 연산자를 이용한, 객체메모리할당시 사용되는 함수.
cout << "operator new : " << size << endl;
void* adr = new char[size];
return adr;
}
void* operator new[](size_t size) { // new 연산자를 이용한, 객체배열할당시 사용되는 함수.
cout << "operator new[] : " << size << endl;
void* adr = new char[size];
return adr;,
}
void operator delete(void* adr) { // delete 연산자를 이용한, 객체메모리소멸시 사용되는 함수
cout << "oeprator delete()" << endl;
delete[]adr;
}
void operator delete[](void *adr) { // delete 연산자를 이용한, 객체배열소멸시 사용되는 함수.
cout << "operator delete[] ()" << endl;
delete[]adr;,
}
};
ostream& operator<<(ostream& os, const Point& pos) { // 객체 출력을 위한, ostream & operator<<함수
os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;
return os;
}
int main() {
Point* ptr = new Point(3, 4);
Point* arr = new Point[3]; // 배열의 생성과정에서 총 3개의 Point 객체가 생성된다. 이 3개의 Point 객체의 할당에 필요한 메모리 공간은, void *operator new[](size_t size)의 맴버함수(static 함수)의 호출에 의해 완성된다.
delete ptr;
delete[]arr; // Point *arr = new Point[3]에서 할당한 배열의 소멸을 명령하고 있다. 객체로 구성된 배열이기 때문에 모든 객체의 소멸자가 호출된 다음에, void operator delete[](void *adr)가 호출된다.
return 0;
}
* 유의사항 - 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다. - 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다. - 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :) |