문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - 연산자 오버로딩 6 : new & delete 연산자 오버로딩 (new 연산자 오버로딩에 대한 상세한 이해, operator new & operator new[ ])

벼리네 2021. 3. 1. 11:06
반응형

[문과 코린이의 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){
  void *adr = new char[size] // 컴파일러에 의해, 필요한 메모리 공간의 크기가 1byte(char 단위)로 할당하여 반환한다.
  return adr;
}

- 즉, 이와 같이 (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으로 선언된 함수이기 때문이다.

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;

}

 

 


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형