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