{{ label!='' ? 'Label : ' : (q!='' ? '검색 : ' : '전체 게시글') }} {{ label }} {{ q }} {{ ('('+(pubs|date:'yyyy-MM')+')') }}

[C++] 스터디 CPPALOM 5주차: 열혈 C++ 프로그래밍 Chap 10-12

(파워 포인트 파일(.pptx)을 Markdown으로 변환하여 업로드하였음)



# <br>연산자 오버로딩

* C++의 모든 연산자는 내부적으로 operator+, operator- 등으로 호출된다.
  * 이는 다음 두 가지 방식으로 오버로딩할 수 있다:
  * 멤버 함수에 의한 연산자 오버로딩
  * 전역 함수에 의한 연산자 오버로딩

```C++
int main(void)
{
    Point pos1(3, 4);
    Point pos2(10, 20);
    Point pos3=pos1.operator+(pos2);
    pos1.ShowPosition();
    pos2.ShowPosition();
    pos3.ShowPosition();
    Point pos4=pos1+pos2;
    pos4.ShowPosition();
    return 0;
}
```

```C++
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+(const Point &ref)    //operator+��� �̸��� �Լ�
    {
        Point pos(xpos+ref.xpos, ypos+ref.ypos);
        return pos;
    }
};
```

```C++
[3, 4]
[10, 20]
[13, 24]
```

* 전역 함수에 의한 연산자 오버로딩
  * 오버로딩된 전역 함수는 Point 객체의 멤버에 접근해야 한다.
  * 따라서 friend 키워드를 이용해 이를 허용한다.

```C++
int main(void)
{
    Point pos1(3, 4);
    Point pos2(10, 20); 
    Point pos3=pos1+pos2;
    pos1.ShowPosition();
    pos2.ShowPosition();
    pos3.ShowPosition();
    return 0;
}
```

```C++
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; 
    }
    friend Point operator+(const Point &pos1, const Point &pos2);
}; 
Point operator+(const Point &pos1, const Point &pos2)
{
    Point pos(pos1.xpos+pos2.xpos, pos1.ypos+pos2.ypos);
    return pos;
} 
```

```C++
[3, 4]
[10, 20]
[13, 24]
```

# <br>오버로딩이 가능한 연산자의 종류

* 가능
* 불가능
  * +형변환 연산자

![](img%5Cweek5_subin0.png)

![](img%5Cweek5_subin1.png)

![](img%5Cweek5_subin2.png)

![](img%5Cweek5_subin3.png)

# <br>오버로딩의 주의사항

* 본래의 의도를 벗어난 형태의 연산자 오버로딩을 피한다.
* 연산자의 우선순위와 결합성은 바뀌지 않는다.
* 매개변수의 디폴트 값 설정이 불가능하다.
* 연산자의 순수 기능까지 빼앗을 수 없다.
  * 가령:

```C++
int operator+(const int num1, const int num2)
{
    return num1 * num2;
}
```

# <br>단항 연산자의 오버로딩

* 전위 증감 연산자의 경우:
  * 멤버 함수로 오버로딩한 경우 매개변수가 없음
  * 전역 함수로 오버로딩한 경우 매개변수가 참조 하나

```C++
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++()
    {
        xpos+=1;
        ypos+=1;
        return *this;
    }
    friend Point& operator--(Point &ref);
};
Point& operator--(Point &ref)
{
    ref.xpos-=1;
    ref.ypos-=1;
    return ref;
}
```

```C++
int main(void)
{
    Point pos(1, 2);
    ++pos;
    pos.ShowPosition();
    --pos;
    pos.ShowPosition(); 
    ++(++pos);
    pos.ShowPosition();
    --(--pos);
    pos.ShowPosition();
    return 0;
}
```

```C++
[2, 3]
[1, 2]
[3, 4]
[1, 2]
```

* 후위 증감 연산자의 경우:
  * 멤버 함수로 오버로딩한 경우 매개변수가 int
  * 전역 함수로 오버로딩한 경우 매개변수가 참조와 int

```C++
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++() 
    {
        xpos+=1;
        ypos+=1;
        return *this;
    }
    const Point operator++(int)
    {
        const Point retobj(xpos, ypos);   // const Point retobj(*this);
        xpos+=1;
        ypos+=1;
        return retobj;
    }
    friend Point& operator--(Point &ref);
    friend const Point operator--(Point &ref, int);
};
```

```C++
int main(void)
{
    Point pos(3, 5);
    Point cpy;
    cpy=pos--;
    cpy.ShowPosition();
    pos.ShowPosition();
    cpy=pos++;
    cpy.ShowPosition(); 
    pos.ShowPosition();
    return 0;
}
```

```C++
Point& operator--(Point &ref)
{
    ref.xpos-=1;
    ref.ypos-=1;
    return ref;
}
const Point operator--(Point &ref, int)
{
    const Point retobj(ref);
    ref.xpos-=1;
    ref.ypos-=1;
    return retobj;
}
```

```C++
[2, 3]
[1, 2]
[3, 4]
[1, 2]
```

```C++
class X 
{
public: // members (with implicit this pointer):
    X* operator&(); // prefix unary & (address of)
    X operator&(X); // binary & (and)
    X operator++(int); // postfix increment
    X operator&(X,X); // error : ternary
    X operator/(); // error : unary /
};
// nonmember functions :
X operator-(X); // prefix unary minus
X operator-(X,X); // binary minus
X operator--(X&,int); // postfix decrement
X operator-(); // error : no operand
X operator-(X,X,X); // error : ternary
X operator%(X); // error : unary 
```

# <br>반환형에서의 const 선언과 const 객체

이 후위 연산은 const 객체를 반환한다.

따라서 다음과 같은 문장이 불가능하다:

해당 함수가 반환하는 객체가 const형이기 때문!

```C++
const Point operator++(int)
{
    const Point retobj(xpos, ypos);
    xpos += 1;
    ypos += 1;
    return retobj;
}
const Point operator--(int)
{
    const Point retobj(xpos, ypos);
    xpos -= 1;
    ypos -= 1;
    return retobj;
}
```

```C++
int main(void)
{
    Point pos(3, 5);
    (pos++)++;
    (pos--)--;
}
```

# <br>교환법칙 문제의 해결

* 곱셈 연산자의 경우 교환법칙이 성립한다.
  * 하지만 멤버 함수의 형태로 오버로딩이 되면, 멤버 함수가 정의된 클래스 객체가 오버로딩 된 연산자의 왼편에 와야 한다!

```C++
#include <iostream>
using namespace std;
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;
    }
};
```

```C++
int main(void)
{
    Point pos(1, 2);
    Point cpy;
    cpy=pos*3;
    cpy.ShowPosition();
    cpy=3*pos; // error!
    cpy.ShowPosition();
    return 0;
}
```

* 따라서 전역 함수 기반으로 이를 해결할 수 있다.
  * 이것이 friend를 사용해야 하는 이유이다!

```C++
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;
}
```

```C++
int main(void)
{
    Point pos(1, 2);
    Point cpy;
    cpy=3*pos;
    cpy.ShowPosition();
    cpy=2*pos*3;
    cpy.ShowPosition();
    return 0;
}
```

# <br>cout, cin, endl

```C++
namespace mystd 
{
    using namespace std;
    class ostream
    {
    public:
        void operator<< (char * str) 
        {
            printf(" _%s_ ", str);
        }
        void operator<< (char str) 
        {
            printf(" _%c_ ", str);
        }
        void operator<< (int num) 
        {
            printf(" _%d_ ", num);
        }   
        void operator<< (double e) 
        {
            printf(" _%g_ ", e);
        }
        void operator<< (ostream& (*fp)(ostream &ostm))
        {
            fp(*this);
        }
    };
    ostream& endl(ostream &ostm)
    {
        ostm<<'\n';
        fflush(stdout);
        return ostm;
    }
    ostream cout;
}
```

* 이들 역시 연산자 오버로딩으로 입출력을 해결하고 있었다.
  * <<, >> 연산자는 계속해서 ostream, istream을 반환한다.
  * 이를 이용해서 우리는 연속해서 입출력을 수행할 수 있다.

```C++
int main(void)
{
    using mystd::cout;
    using mystd::endl;
    cout<<"Simple String";
    cout<<endl;
    cout<<3.14;
    cout<<endl;
    cout<<123;
    endl(cout);
    return 0;
}
```

# <br><<, >> 연산자의 오버로딩

```C++
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; 
    }
    friend ostream& operator<<(ostream&, const Point&);
};
ostream& operator<<(ostream& os, const Point& pos)
{
    os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl; 
    return os;
}
```

* cout, cin의 소스를 수정할 수 없다.
  * 따라서, 전역 함수를 이용해서 오버로딩을 한다.
* 이를 이용해 사용자 정의 객체를 ostream으로 << 연산하였을 때, 객체의 정보가 나타나게 할 수 있다.
  * Java의 toString()을 C++에서는 이렇게 구현한다.

```C++
int main(void)
{
    Point pos1(1, 3);
    cout<<pos1;
    Point pos2(101, 303);
    cout<<pos2;
    return 0;
}
```

```C++
[1, 3]
[101, 303]
```

# <br>디폴트 대입 연산자

대입 연산자 역시 오버로딩이 가능하며, 디폴트 대입 연산자가 존재한다.

```C++
class First
{
private:
    int num1, num2;
public:
    First(int n1=0, int n2=0) : num1(n1), num2(n2)
    {  }
    void ShowData() { cout<<num1<<", "<<num2<<endl; }
};
class Second
{
private:
    int num3, num4;
public:
    Second(int n3=0, int n4=0) : num3(n3), num4(n4)
    {  }
    void ShowData() { cout<<num3<<", "<<num4<<endl; }
    Second& operator=(const Second& ref)
    {
        cout<<"Second& operator=()"<<endl;
        num3=ref.num3;
        num4=ref.num4;
        return *this;
    }
};
```

```C++
int main(void)
{
    First fsrc(111, 222);
    First fcpy;
    Second ssrc(333, 444);
    Second scpy;
    fcpy=fsrc;
    scpy=ssrc;
    fcpy.ShowData();
    scpy.ShowData();
    First fob1, fob2;
    Second sob1, sob2;
    fob1=fob2=fsrc;
    sob1=sob2=ssrc;
    fob1.ShowData();
    fob2.ShowData();
    sob1.ShowData();
    sob2.ShowData();
    return 0;
}
```

```C++
Second& operator=()
111, 222
333, 444
Second& operator=()
Second& operator=()
111, 222
111, 222
333, 444
333, 444
```

# <br>디폴트 대입 연산자의 문제점

* 이 역시 디폴트 복사 생성자에서 보인 문제점과 유사하다.
  * 얕은 복사로 인해 문제가 발생할 수 있다!
* 소멸자의 호출 과정에서 이미 소멸한 객체를 소멸시키면서 문제가 발생한다.
* 다음과 같이 해결할 수 있다:

```C++
class Person
{
    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;
    }
};
```

```C++
int main(void)
{
    Person man1("Lee dong woo", 29);
    Person man2("Yoon ji yul", 22);
    man2=man1;
    man1.ShowPersonInfo();
    man2.ShowPersonInfo();
    return 0;
}
```

```C++
Person& Person::operator=(const Person& ref)
{
    delete []name;
    int len=strlen(ref.name)+1;
    name= new char[len];
    strcpy(name, ref.name);
    age=ref.age;
    return *this;
}
```

```C++
이름: Lee dong woo
나이: 29
이름: Lee dong woo
나이: 29
called destructor!
```

# <br>상속 구조에서의  대입 연산자

```C++
class First
{
private:
    int num1, num2;
public:
    First(int n1=0, int n2=0) : num1(n1), num2(n2)
    {  }
    void ShowData() { cout<<num1<<", "<<num2<<endl; }
    First& operator=(const First&ref)
    {
        cout<<"First& operator=()"<<endl;
        num1=ref.num1;
        num2=ref.num2;
        return *this;
    }
};
class Second : public First
{
private:
    int num3, num4;
public:
    Second(int n1, int n2, int n3, int n4) 
        : First(n1, n2), num3(n3), num4(n4)
    {  }
    void ShowData() 
    {
        First::ShowData();
        cout<<num3<<", "<<num4<<endl; 
    }    
};
```

유도 클래스의 디폴트 대입 연산자는 기초 클래스의 대입 연산자 역시 호출한다.

하지만 유도 클래스의 대입 연산자를 오버로딩하면, 기초 클래스의 대입 연산자를 명시적으로 호출해줘야 한다:

```C++
Second& operator=(const Second &ref)
{
    cout<<"Second& operator=()"<<endl;
    First::operator=(ref);
    num3=ref.num3;
    num4=ref.num4;
    return *this;
}
```

```C++
int main(void)
{
    Second ssrc(111, 222, 333, 444);
    Second scpy(0, 0, 0, 0);
    scpy=ssrc;
    scpy.ShowData();
    return 0;
}
```

```C++
First& operator=()
111, 222
333, 444
```

# <br>멤버 초기자의 성능 향상 확인

```C++
class AAA
{
private:
    int num;
public:
    AAA(int n=0): num(n)
    {  
        cout<<"AAA(int n=0)"<<endl;
    }
    AAA(const AAA &ref): num(ref.num)
    {  
        cout<<"AAA(const AAA & ref)"<<endl;
    }
    AAA & operator=(const AAA &ref)
    {
        num=ref.num;
        cout<<"operator=(const AAA &ref)"<<endl;
        return *this;
    }
};class BBB
{
private:
    AAA mem;
public:
    BBB(const AAA & ref)
        : mem(ref)
    {  }
};class CCC
{
private:
    AAA mem;
public:
    CCC(const AAA & ref)
    {
        mem=ref;
    }
};
```

CCC 클래스의 경우 인자를 대입하는 연산이 추가로 호출된다.

BBB의 경우 즉시 AAA의 생성자를 호출하게 할 수 있으므로, 연산이 단순해진다.

```C++
int main(void)
{
    AAA obj1(12);
    cout<<"*********************"<<endl;
    BBB obj2(obj1);
    cout<<"*********************"<<endl;
    CCC obj3(obj1);
    return 0;
}
```

```C++
AAA(int n=0)*********************
AAA(const AAA& ref)
*********************
AAA(int n=0)
operator=(const AAA& ref)
```

# <br>인덱스 연산자 오버로딩

* C, C++의 배열 접근은 경계 검사를 하지 않는다.
  * error-prone!
* operator[]를 오버로딩함으로써, 이러한 경계 검사를 수행할 수 있다.

```C++
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;
    }
};
```

```C++
int main(void)
{
    BoundCheckIntArray arr(5);
    for(int i=0; i<5; i++)
        arr[i]=(i+1)*11;
    for(int i=0; i<6; i++)
        cout<<arr[i]<<endl;
    return 0;
}
```

```C++
11
22
33
44
55
Array index out of boud exception
```

# <br>const 오버로딩 활용

```C++
class BoundCheckIntArray 
{
private:
    int * arr;
    int arrlen;
    BoundCheckIntArray(const BoundCheckIntArray& arr) { }
    BoundCheckIntArray& operator=(const BoundCheckIntArray& arr) { }
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];
    }
    int GetArrLen() const 
    {
        return arrlen;
    }
    ~BoundCheckIntArray()
    {
        delete []arr;
    }
```

operator[]는 const가 아니므로, ShowAllData()에서 호출될 수 없다.

```C++
void ShowAllData(const BoundCheckIntArray& ref)
{
    int len=ref.GetArrLen();
    for(int idx=0; idx<len; idx++)
        cout<<ref[idx]<<endl; // compile error!
}
```

```C++
int main(void)
{
    BoundCheckIntArray arr(5);
    for(int i=0; i<5; i++)
        arr[i]=(i+1)*11; 
    ShowAllData(arr); // compile error!
    return 0;
}
```

```C++
class BoundCheckIntArray 
{
private:
    int * arr;
    int arrlen;
    BoundCheckIntArray(const BoundCheckIntArray& arr) { }
    BoundCheckIntArray& operator=(const BoundCheckIntArray& arr) { }
public:
    ...
    int& operator[] (int idx)
    {
        if(idx<0 || idx>=arrlen)
        {
            cout<<"Array index out of bound exception"<<endl;
            exit(1);
        }
        return arr[idx];
    }
    int operator[] (int idx) const 
    {
        if(idx<0 || idx>=arrlen)
        {
            cout<<"Array index out of bound exception"<<endl;
            exit(1);
        }
        return arr[idx];
    }
    ...
};
```

* 따라서 const를 추가한 함수를 하나 더 오버로딩 한다!
  * const 역시 함수 시그니처에 포함되므로, 두 개를 오버로딩해야 한다.

```C++
void ShowAllData(const BoundCheckIntArray& ref)
{
    int len=ref.GetArrLen();
    for(int idx=0; idx<len; idx++)
        cout<<ref[idx]<<endl;
}
```

```C++
int main(void)
{
    BoundCheckIntArray arr(5);
    for(int i=0; i<5; i++)
        arr[i]=(i+1)*11; 
    ShowAllData(arr);
    return 0;
}
```

# <br>객체 배열 클래스

```C++
class BoundCheckPointArray 
{
private:
    Point * arr;
    int arrlen;
    BoundCheckPointArray(const BoundCheckPointArray& arr) { }
    BoundCheckPointArray& operator=(const BoundCheckPointArray& arr) { }
public:
    BoundCheckPointArray(int len) :arrlen(len)
    {
        arr=new Point[len];
    }
    Point& operator[] (int idx)
    {
        if(idx<0 || idx>=arrlen)
        {
            cout<<"Array index out of bound exception"<<endl;
            exit(1);
        }
        return arr[idx];
    }
    Point operator[] (int idx) const 
    {
        if(idx<0 || idx>=arrlen)
        {
            cout<<"Array index out of bound exception"<<endl;
            exit(1);
        }
        return arr[idx];
    }
};
```

다음 코드는 객체의 저장을 객체 간의 대입 연산으로  수행한다.

```C++
int main(void)
{
    BoundCheckPointArray arr(3);
    arr[0]=Point(3, 4);
    arr[1]=Point(5, 6);
    arr[2]=Point(7, 8);
    for(int i=0; i<arr.GetArrLen(); i++)
        cout<<arr[i];
    return 0;
}
```

```C++
[3, 4]
[5, 6]
[7, 8]
```

```C++
typedef Point * POINT_PTR;
class BoundCheckPointPtrArray 
{
private:
    POINT_PTR * arr;
    int arrlen;
    BoundCheckPointPtrArray(const BoundCheckPointPtrArray& arr) { }
    BoundCheckPointPtrArray& operator=(const BoundCheckPointPtrArray& arr) { }
public:
    BoundCheckPointPtrArray(int len) :arrlen(len)
    {
        arr=new POINT_PTR[len];
    }
    POINT_PTR& operator[] (int idx)
    {
        if(idx<0 || idx>=arrlen)
        {
            cout<<"Array index out of bound exception"<<endl;
            exit(1);
        }
        return arr[idx];
    }
    POINT_PTR operator[] (int idx) const 
    {
        if(idx<0 || idx>=arrlen)
        {
            cout<<"Array index out of bound exception"<<endl;
            exit(1);
        }
        return arr[idx];
    }
};
```

다음 코드는 객체의 주소 값을 이용해서 값을 관리한다.

```C++
int main(void)
{
    BoundCheckPointPtrArray arr(3);
    arr[0]=new Point(3, 4);
    arr[1]=new Point(5, 6);
    arr[2]=new Point(7, 8);
    for(int i=0; i<arr.GetArrLen(); i++)
        cout<<*(arr[i]);
    delete arr[0];
    delete arr[1];
    delete arr[2];
    return 0;
}
```

```C++
[3, 4]
[5, 6]
[7, 8]
```

# <br>new, delete 연산자 오버로딩

* 기본적으로 제공되는 new 연산자가 하는 일은 다음과 같다:
  * 메모리 공간의 할당
  * 생성자의 호출
  * 할당하고자 하는 자료형에 맞게 반환된 주소 값의 형 변환
* 하지만 new 연산자를 오버로딩하면, 메모리 공간의 할당만 오버로딩할 수 있다.
* delete 역시 오버로딩할 수 있으며, 메모리 공간의 소멸을 책임진다.
  * void 포인터 형 대상의 delete 연산을 허용하지 않는다면, char 포인터 형으로 변환해서 delete 연산을 진행하면 된다.

* 객체 생성이 완성된 상태가 아닌데, 오버로딩된 new 멤버 함수의 호출이 가능한 이유:
  * 이들은 static으로 선언된 함수이다.

```C++
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 delete (void * adr)
    {
        cout<<"operator delete ()"<<endl;
        delete []adr;
    }
};
ostream& operator<<(ostream& os, const Point& pos)
{
    os<<'['<<pos.xpos<<", "<<pos.ypos<<']'<<endl;
    return os;
}
```

```C++
int main(void)
{
    Point * ptr=new Point(3, 4);
    cout<<*ptr;
    delete ptr;
    return 0;
}
```

```C++
operator new : 8
[3, 4]
operator delete ()
```

```C++
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;
    }
};
```

* new[]와 delete[] 역시 유사하다.
  * 괄호 안의 숫자를 이용해서 사이즈를 계산해 해당 함수의 인자로 넘겨준다.

```C++
int main(void)
{
    Point * ptr=new Point(3, 4);
    Point * arr=new Point[3];
    delete ptr;
    delete []arr;
    return 0;
}
```

```C++
operator new : 8
operator new [] : 24
operator delete ()
operator delete[] ()
```

# <br>포인터 연산자 오버로딩

```C++
class Number
{
private:
    int num;
public:
    Number(int n) : num(n) {  }
    void ShowData() { cout<<num<<endl; }
    Number * operator->()
    {
        return this;
    }
    Number & operator*()
    {
        return *this;
    }
};
```

* ->, * 역시 오버로딩 할 수 있다.
  * -> : 포인터가 가리키는 객체의 멤버에 접근
  * *  : 포인터가 가리키는 객체에 접근
* 주의점:
  * -> 연산자의 경우 객체 그 자체를 넘기면 된다.
  * num->ShowData();
  * num.operator->() ShowData();
  * num.operator->() -> ShowData();
  * 중간의 과정에서 문법적으로 성립하는 부분이 있으므로, 컴파일러는 자동적으로 -> 연산자를 하나 더 추가하여 해석을 진행한다.

```C++
int main(void)
{
    Number num(20);
    num.ShowData();
    (*num)=30;
    num->ShowData();
    (*num).ShowData();
    return 0;
}
```

# <br>펑터

```C++
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;
}
```

Functor(a.k.a Function Object)

operator() 오버로딩을 이용해서 함수처럼 동작하게 만드는 객체

```C++
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;
    }
};
```

```C++
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;
}
```

# <br>펑터 예시

```C++
// sort algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::sort
#include <vector>       // std::vector
bool myfunction (int i,int j) { return (i<j); }
struct myclass {
  bool operator() (int i,int j) { return (i<j);}
} myobject;
int main () {
  int myints[] = {32,71,12,45,26,80,53,33};
  std::vector<int> myvector (myints, myints+8);               // 32 71 12 45 26 80 53 33
  // using default comparison (operator <):
  std::sort (myvector.begin(), myvector.begin()+4);           //(12 32 45 71)26 80 53 33
  // using function as comp
  std::sort (myvector.begin()+4, myvector.end(), myfunction); // 12 32 45 71(26 33 53 80)
  // using object as comp
  std::sort (myvector.begin(), myvector.end(), myobject);     //(12 26 32 33 45 53 71 80)
  // print out content:
  std::cout << "myvector contains:";
  for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';
  return 0;
}
```

이를 이용해서 마치 인터페이스처럼 다룰 수 있다.

일종의 Strategy Pattern

# <br>형 변환 연산자

```C++
class Number
{
private:
    int num;
public:
    Number(int n=0) : num(n)
    {
        cout<<"Number(int n=0)"<<endl;
    }
    Number& operator=(const Number &ref)
    {
        cout<<"operator=()"<<endl;
        num=ref.num;
        return *this;
    }
    operator int ()    // 형 변환 연산자의 오버로딩
    {
        return num;
    }
    void ShowNumber() { cout<<num<<endl; }
};
int main(void)
{
    Number num1;
    num1=30;
    Number num2=num1+20;
    num2.ShowNumber();
    return 0;
}
```

* 원시 타입으로도 변환할 수 있다.
  * num1+20을 하기 위해 num1은 자동으로 int 형으로 변환된다.
  * 이 때, 먼저 num1의 operator+()가 호출된다.
    * 이 경우 존재하지 않으므로,
  * operator int ()가 호출되어 num1을 int로 변환한다.

```C++
Number(int n=0)
Number(int n=0)
operator=()
Number(int n=0)
30
```

* 심지어 다음도 가능하다!
  * 두 개의 인자 역시 중괄호를 이용해 명시하면 자동으로 형 변환이 수행된다.

```C++
#include <iostream>
using namespace std;
class Number
{
private:
    int num;
    int num2;
public:
    Number(int n) : num(n)
    {
        cout<<"Number(int n=0)"<<endl;
    }
    Number(int n, int n2) : num(n), num2(n2)
    {
        cout<<"Number(int n, int n2)"<<endl;
    }
    Number& operator=(const Number &ref)
    {
        cout<<"operator=()"<<endl;
        num=ref.num;
        num2=ref.num2;
        return *this;
    }
    void ShowNumber() { cout<<num<< "," << num2 <<endl; }
};
```

```C++
int main(void)
{
    Number num(50);
    num = {10, 20};
    num.ShowNumber();
    return 0;
}
```

```C++
Number(int n=0)
Number(int n, int n2)
operator=()
10,20
```

# <br>string

* C++ 표준 라이브러리에는 string 클래스가 정의되어 있다.
  * char* 대신, string 클래스를 이용해서 문자열을 다루는 것이 권장된다.

```C++
#include <iostream>
#include <string>
using namespace std;
int main(void)
{
    string str1="I like ";
    string str2="string class";
    string str3=str1+str2;
    cout<<str1<<endl;
    cout<<str2<<endl;
    cout<<str3<<endl;
    str1+=str2;
    if(str1==str3)  
        cout<<"동일 문자열!"<<endl;
    else
        cout<<"동일하지 않은 문자열!"<<endl;
    string str4;
    cout<<"문자열 입력: ";
    cin>>str4;
    cout<<"입력한 문자열: "<<str4<<endl;
    return 0;
}
```

```C++
I like
string class
I like string class
동일 문자열!
문자열 입력: Hi~
입력한 문자열: Hi~
```

# <br>표준 string 클래스의 분석

* 문자열을 인자로 전달받는 생성자의 정의
  * 둘은 동치이다:
* 이는 다음과 같이 정의될 것이다:

```C++
string str1="I like ";
string str2="string class";
```

```C++
string str1("I like ");
string str2("string class");
```

```C++
String::String(const char* s)
{
    len=strlen(s)+1;
    str=new char[len];
    strcpy(str, s);
}
```

* 생성자, 소멸자, 복사 생성자, 대입 연산자의 정의
  * 문자열의 길이는 일정치 않으므로, 메모리 공간을 생성자 내에서 동적 할당해야 한다.
  * 이로 인해서 소멸자를 정의해야 하며, 깊은 복사를 하는 복사 생성자와 대입 연산자까지 함께 정의해야 한다.

```C++
String::String()
{
    len=0;
    str=NULL;
}
String::String(const String& s)
{
    len=s.len;
    str=new char[len];
    strcpy(str, s.str);
}
String::~String() 
{
    if(str!=NULL)
        delete []str; 
}
String& String::operator= (const String& s)
{
    if(str!=NULL)
        delete []str;
    len=s.len;
    str=new char[len];
    strcpy(str, s.str);
    return *this;
}
```

* 결합된 문자열로 초기화된 객체를 반환하는 + 연산자의 오버로딩
  * 이는 str3에 str1과 str2가 이어진 문자열이 저장된다.
* 정의는 다음과 같을 것이다:

```C++
String str3=str1+str2;
```

```C++
String String::operator+ (const String& s)
{
    char* tempstr=new char[len+s.len-1];
    strcpy(tempstr, str);
    strcat(tempstr, s.str);
    String temp(tempstr);
    delete []tempstr;
    return temp;
}
```

* 문자열을 덧붙이는 += 연산자의 오버로딩
  * 이는 str1의 문자열에 str2의 문자열이 덧붙여진 형태로 저장된다.
* 정의는 다음과 같을 것이다:

```C++
String& String::operator+= (const String& s)
{
    len+=(s.len-1);
    char* tempstr=new char[len];
    strcpy(tempstr, str);
    strcat(tempstr, s.str);
    if(str!=NULL)
        delete []str;
    str=tempstr;
    return *this;
}
```

* 내용 비교를 진행하는 == 연산자의 오버로딩
  * == 연산을 통해 두 문자열이 동일한지 아닌지를 알 수 있다.
* 정의는 다음과 같을 것이다:

```C++
if(str1==str3)     
    cout<<"동일 문자열!"<<endl;
else
    cout<<"동일하지 않은 문자열!"<<endl;
```

```C++
bool String::operator== (const String& s)
{
    return strcmp(str, s.str) ? false : true;
}
```

* 콘솔 입출력이 가능하도록 <<, >> 연산자의 오버로딩
  * << 연산자를 이용한 출력과 >> 연산자를 이용한 입력이 가능해야 한다.
* 정의는 다음과 같을 것이다:

```C++
String str4;
cout<<"문자열 입력: ";
cin>>str4;
cout<<"입력한 문자열: "<<str4<<endl;
```

```C++
ostream& operator<< (ostream& os, const String& s)
{
    os<<s.str;
    return os;
}
istream& operator>> (istream& is, String& s)
{
    char str[100];
    is>>str;
    s=String(str);
    return is;
}
```

댓글

이 블로그의 인기 게시물

[코딩의탑] 4층: 툰 쉐이딩

[코딩의탑] 3층: 바다 렌더링

[코딩의탑] 5층: 포탈(Portal), 더 나아가기