[열혈 c++ 프로그래밍] ch10 & ch11

hyng·2023년 3월 26일
0

열혈 c++ 프로그래밍

목록 보기
11/12

열혈 c++ 프로그래밍을 보고 요약정리합니다.

연산자 오버로딩의 이해와 유형

  • pos1 + pos2 → pos1.operator+(pos2)
  • 연산자를 오버로딩 하는 방법에는 두 가지가 있다.
    • 멤버함수에 의한 연산자 오버로딩
      • pos1.operator+(pos2)
    • 전역함수에 의한 연산자 오버로딩
      • operator+(pos1,pos2)
  • ++pos
    • 멤버함수로 오버로딩
      • pos.operator++();
    • 전역함수로 오버로딩 된 경우
      • operator++(pos);
  • 교환법칙이 성립하도록 연산자 오버로딩을 구현해보자
    class Point {
    private:
        int xpos, ypos;
    public:
        Point(int x = 0, int y = 0) : xpos(x), ypos(y)
        {}
        void ShowPosition() const
        {
            cout << '[' << xpos << ", " << ypos << ']' << endl;
        }
        Point operator*(int times)
        {
            Point pos(xpos*times, ypos*times);
            return pos;
        }
        friend Point operator*(int times, Point& ref);
    };
    
    Point operator*(int times, Point& ref)
    {
        return ref * times;
    }
    
    int main(void)
    {
        Point pos(1, 2);
        Point cpy;
        
        cpy = 3 * pos;
        cpy.ShowPosition();
        
        cpy = 2 * pos * 3;
        cpy.ShowPosition();
        return 0;
    }

  • 대입 연산자는 기존에 이미 생성 및 초기화가 진행된 객체일 경우 호출된다.
    int main(void)
    {
    	Point pos1(5, 7);
    	Point pos2(9, 10);
    	pos2 = pos1; // pos2.operator=(pos1)로 해석된다.
    }
  • 디폴트 대입 연산자의 문제점
    • 디폴트 복사 생성자에서 나왔던 것처럼 앝은 복사로 인해 문제가 발생한다.
      class Person
      {
      private:
          char * name;
          int age;
      public:
          Person(char * myname, int myage)
          {
              int len = strlen(myname) + 1;
              name = new char[len];
              strcpy(name, myname);
              age = myage;
          }
          void ShowPersonInfo() const
          {
              cout << "이름: " << name << endl;
              cout << "나이: " << age << endl;
          }
          ~Person()
          {
              delete []name;
              cout << "called destructor!" << endl;
          }
      };
      int main(void)
      {
          Person man1("JUNG", 29);
          Person man2("LEE", 30);
          man1 = man2;
          return 0;
      }
      • “LEE” 가리키는 포인터가 “JUNG”을 가리키게 되면서 메모리 누수 발생
      • man1 소멸 후 같은 “JUNG” 메모리를 가리키는 man2는 이미 소멸된 메모리 주소를 가리키게 됨
      • man2가 소멸할 때 이미 소멸된 메모리 중복 소멸
    • 이러한 문제를 해결하기 위해 깊은 복사를 진행한다.
  • 유도 클래스의 대입 연산자 정의에서 명시적으로 기초 클래스의 대입 연산자 호출문을 삽입하지 않으면, 기초 클래스의 대입 연산자는 호출되지 않아서 기초 클래스의 멤버 변수는 멤버 대 멤버 복사 대상에서 제외된다.
  • 배열 클래스
    • c, c++ 배열은 경계검사를 하지 않는다는 단점이 있다.

      int arr[3] = {1, 2, 3};
      cout << arr[-1] << end; //arr의 주소 + sizeof(int) x -1 의 위치에 접근
      cout << arr[-2] << endl; //arr의 주소 + sizeof(int) x -2 의 위치에 접근
    • 이러한 단점을 해결하기 위해 배열 클래스라는 것을 사용할 수 있다.

      class BoundCheckIntARRAY
      {
      private:
          int * arr;
          int arrlen;
      public:
          BoundCheckIntARRAY(int len) : arrlen(len)
          {
              arr = new int[len];
          }
          int& operator[] (int idx)
          {
              if (idx < 0 || idx >= arrlen)
              {
                  cout << "Array index out of bound exception" << endl;
                  exit(1);
              }
              return arr[idx];
          }
      
          ~BoundCheckIntARRAY()
          {
              delete []arr;
          }
      };
    • 복사 생성자와 대입 연산자를 private로 선언해서 복사 또는 대입을 원천적으로 막을 수도 있다.

      int main(void)
      {
      	BoundCheckIntArray arr(5);
        for (int i = 0; i < 5; i++) {
      		arr[i] = (i + 1) * 11;
      	}
      	BoundCheckIntArray cpy1(5);
      	cpy1 = arr; //안전하지 않은 코드
      	BoundCheckIntArray copy = arr; //안전하지 않은 코드
        ...
      }
    • 스마트 포인터

      • 라이브러리에서 제공하는 스마트 포인터를 덜 똑똑한 버전으로 직접 구현해 보자
        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)
            {}
            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));
            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 객체 생성
        Point 객체 생성
        Point 객체 생성
        [1, 2]
        [2, 3]
        [4, 5]
        [10, 20]
        [30, 40]
        [50, 60]
        Point 객체 소멸
        Point 객체 소멸
        Point 객체 소멸
        • Point 객체 소멸을 위한 delete 연산이 자동으로 이뤄졌다.
profile
공부하고 알게 된 내용을 기록하는 블로그

0개의 댓글