4 minute read

3. 그 이외의 연산자 오버로딩

  • newdelete도 연산자이기 때문에 오버로딩이 가능하다
  • 스마트 포인터펑터라는 것도 있다

✏️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 newoperator 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클래스와 같이 함수처럼 동작하는 클래스를 펑터라고 하며 함수 오브젝트라고도 불린다.
    • 펑터는 함수 똔느 객체의 동작방식에 유연함을 제공할 때 사용된다.

Tags: ,

Categories:

Updated: