[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>오버로딩이 가능한 연산자의 종류
* 가능
* 불가능
* +형변환 연산자




# <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;
}
```
댓글
댓글 쓰기