7 장 프렌드와 연산자 중복 1 명품 C++
친구란 ? 2 우리 집 냉장고 내 침대 우리 집 TV 우리 집 식탁 친구 친구 ? 내 가족의 일원은 아니지만 내 가족과 동일한 권한을 가진 일원으로 인정받은 사람
C++ 프렌드 프렌드 함수 클래스의 멤버 함수가 아닌 외부 함수 전역 함수 다른 클래스의 멤버 함수 friend 키워드로 클래스 내에 선언된 함수 클래스의 모든 멤버를 접근할 수 있는 권한 부여 프렌드 함수라고 부름 프렌드 선언의 필요성 : cout << …, 2 + person, 클래스의 멤버로 선언하기에는 무리가 있고, 클래스의 모든 멤버를 자유롭 게 접근할 수 있는 일부 외부 함수 작성 시 3
프렌드로 초대하는 3 가지 유형 프렌드 함수가 되는 3 가지 전역 함수 : 클래스 외부에 선언된 전역 함수 다른 클래스의 멤버 함수 : 다른 클래스의 특정 멤버 함수 다른 클래스 전체 : 다른 클래스의 모든 멤버 함수 4 전역 함수
프렌드 선언 3 종류 5 class Rect { friend bool RectManager::equals(Rect r, Rect s); }; 2. RectManager 클래스의 equals() 멤버 함수를 Rect 클래스에 프렌드로 선언 class Rect { // Rect 클래스 선언... friend bool equals(Rect r, Rect s); }; 1. 외부 함수 equals() 를 Rect 클래스에 프렌드로 선언 class Rect { friend RectManager; }; 3. RectManager 클래스의 모든 멤버 함수를 Rect 클래스에 프렌드로 선언
예제 7–1 프렌드 함수 만들기 6 #include using namespace std; class Rect; bool equals(Rect r, Rect s); // equals() 함수 선언 class Rect { // Rect 클래스 선언 int width, height; public: Rect(int width, int height) { this->width = width; this->height = height;} friend bool equals(Rect r, Rect s); }; bool equals(Rect r, Rect s) { // 외부 함수 if(r.width == s.width && r.height == s.height) return true; else return false; } int main() { Rect a(3,4), b(4,5); if(equals(a, b)) cout << "equal" << endl; else cout << "not equal" << endl; } not equal equals() 함수를 프렌드로 선언 equals() 함수는 private 속성을 가진 width, height 에 접근할 수 있다. Rect 클래스가 선언되기 전에 먼저 참조 되는 컴파일 오류 (forward reference) 를 막기 위한 선언문 객체 a 와 b 는 동일한 크기의 사각 형이므로 “not equal” 출력
예제 7–2 다른 클래스의 멤버 함수를 프렌드로 선 언 7 #include using namespace std; class Rect; class RectManager { // RectManager 클래스 선언 public: bool equals(Rect r, Rect s); }; class Rect { // Rect 클래스 선언 int width, height; public: Rect(int width, int height) { this->width = width; this->height = height; } friend bool RectManager::equals(Rect r, Rect s); }; bool RectManager::equals(Rect r, Rect s) { if(r.width == s.width && r.height == s.height) return true; else return false; } int main() { Rect a(3,4), b(3,4); RectManager man; if(man.equals(a, b)) cout << "equal" << endl; else cout << "not equal" << endl; } equal RectManager 클래스의 equals() 멤버를 프렌드로 선언 객체 a 와 b 는 동일한 크기의 사 각형이므로 “equal” 출력 Rect 클래스가 선언되기 전에 먼저 참조 되는 컴파일 오류 (forward reference) 를 막기 위한 선언문 RectManager 클 래스는 Rect 클래 스의 private 자료 에 접근이 가능함
예제 7–3 다른 클래스 전체를 프렌드로 선언 8 #include using namespace std; class Rect; class RectManager { // RectManager 클래스 선언 public: bool equals(Rect r, Rect s); void copy(Rect& dest, Rect& src); }; class Rect { // Rect 클래스 선언 int width, height; public: Rect(int width, int height) { this->width = width; this->height = height; } friend RectManager; }; bool RectManager::equals(Rect r, Rect s) { // r 과 s 가 같으면 true 리턴 if(r.width == s.width && r.height == s.height) return true; else return false; } void RectManager::copy(Rect& dest, Rect& src) { // src 를 dest 에 복사 dest.width = src.width; dest.height = src.height; } int main() { Rect a(3,4), b(5,6); RectManager man; man.copy(b, a); // a 를 b 에 복사한다. if(man.equals(a, b)) cout << "equal" << endl; else cout << "not equal" << endl; } equal 객체 b 의 width, height 값이 a 와 같아진다. man.copy(b,a) 를 통해 객체 b 와 a 의 크기가 동일 하므로 “equal” 출력 Rect 클래스가 선언되기 전에 먼저 참조 되는 컴파일 오류 (forward reference) 를 막기 위한 선언문 RectManager 클래스를 프렌드 함수로 선언
연산자 중복 일상 생활에서의 기호 사용 + 기호의 사례 숫자 더하기 : = 5 색 혼합 : 빨강 + 파랑 = 보라 생활 : 남자 + 여자 = 결혼 + 기호를 숫자와 물체에 적용, 중복 사용 + 기호를 숫자가 아닌 곳에도 사용 간결한 의미 전달 다형성 C++ 언어에서도 연산자 중복 가능 C++ 언어에 본래부터 있든 연산자에 새로운 의미 정의 높은 프로그램 가독성 9
연산자 중복의 사례 : + 연산자에 대해 정수 더하기 문자열 합치기 색 섞기 배열 합치기 10 int a=2, b=3, c; c = a + b; // + 결과는 5. 정수가 피연산자일 때 2 와 3 을 더하기 string a="C", c; c = a + "++“; // + 결과는 “C++". 문자열이 피연산자일 때 두 개의 문자열 합치기 Color a(BLUE), b(RED), c; c = a + b; // c = VIOLET. a, b 의 두 색을 섞은 새로운 Color 객체 c SortedArray a(2,5,9), b(3,7,10), c; c = a + b; // c = {2,3,5,7,9,10}. 정렬된 두 배열을 결합한 (merge) 새로운 배열 생성
연산자 중복의 특징 C++ 에 본래 있는 연산자만 중복 가능 3%5 // 컴파일 오류 6## 7 // 컴파일 오류 피 연산자 타입이 다른 새로운 연산 정의 연산자는 함수 형태로 구현 - 연산자 함수 (operator function) 반드시 클래스와 관계를 가짐 피연산자의 개수를 바꿀 수 없음 연산의 우선 순위 변경 안됨 모든 연산자가 중복 가능하지 않음 11 중복 가능한 연산자 중복 불가능한 연산자
연산자 함수 연산자 함수 구현 방법 2 가지 1. 클래스의 멤버 함수로 구현 2. 외부 함수로 구현하고 클래스에 프렌드 함수로 선언 연산자 함수 형식 사례 12 리턴타입 operator 연산자 ( 매개변수리스트 ); Color a(BLUE), b(RED), c; c = a + b; // a 와 b 를 더하기 위한 + 연산자 작성 필요 if(a == b) { // a 와 b 를 비교하기 위한 == 연산자 작성 필요... }
+ 와 == 연산자의 작성 사례 13 class Color {... Color operator+ (Color op2); bool operator== (Color op2); }; 클래스의 멤버 함수로 작성되는 경우 Color operator + (Color op1, Color op2) {... } bool operator == (Color op1, Color op2) {... } class Color {... friend Color operator+ (Color op1, Color op2); friend bool operator== (Color op1, Color op2); }; 외부 함수로 구현되고 클래스에 프렌드로 선언되는 경우
연산자 중복 설명에 사용할 클래스 14 class Power { // 에너지를 표현하는 파워 클래스 int kick; // 발로 차는 힘 int punch; // 주먹으로 치는 힘 public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } };
멤버 함수로 이항 연산자 구현 15
이항 연산자 중복 : + 연산자 16 c = a + b; class Power { int kick; int punch; public: Power operator+ (Power op2); }; c = a. + ( b ); 컴파일러에 의한 변환 리턴 타입 오른쪽 피연산자 b 가 op2 에 전달 Power Power::operator+(Power op2) { Power tmp; tmp.kick = this->kick + op2.kick; tmp.punch = this->punch + op2.punch; return tmp; } Power a + 연산자 함수 코드
예제 7-4 두 개의 Power 객체를 더하는 + 연산자 작성 17 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); Power operator+ (Power op2); // + 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power Power::operator+(Power op2) { Power tmp; // 임시 객체 생성 tmp.kick = this->kick + op2.kick; // kick 더하기 tmp.punch = this->punch + op2.punch; // punch 더하기 return tmp; // 더한 결과 리턴 } int main() { Power a(3,5), b(4,6), c; c = a + b; // 파워 객체 + 연산 a.show(); b.show(); c.show(); } kick=3,punch=5 kick=4,punch=6 kick=7,punch=11 + 연산자 멤버 함수 구현 객체 a, b, c 순 으로 출력 객체 a 의 operator+() 멤버 함수 호출
== 연산자 중복 18 class Power { public: bool operator== (Power op2); }; bool Power::operator==(Power op2) { if(kick==op2.kick && punch==op2.punch) return true; else return false; } a == b a. == ( b ) 컴파일러에 의한 변환 Power a 리턴 타입 오른쪽 피연산자 b 가 op2 에 전달 == 연산자 함수 코드
예제 7-5 두 개의 Power 객체를 비교하는 == 연 산자 작성 19 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); bool operator== (Power op2); // == 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } bool Power::operator==(Power op2) { if(kick==op2.kick && punch==op2.punch) return true; else return false; } int main() { Power a(3,5), b(3,5); // 2 개의 동일한 파워 객체 생성 a.show(); b.show(); if(a == b) cout << " 두 파워가 같다." << endl; else cout << " 두 파워가 같지 않다." << endl; } kick=3,punch=5 두 파워가 같다. == 연산자 멤버 함수 구현 operator==() 멤버 함수 호출
+= 연산자 중복 20 class Power { public: Power operator+= (Power op2); }; c = a += b; c = a. += ( b ); 컴파일러에 의한 변환 Power Power::operator+=(Power op2) { kick = kick + op2.kick; punch = punch + op2.punch; return *this; } Power a 리턴 타입 오른쪽 피연산자 b 가 op2 에 전달 += 연산자 함수 코드 주목
예제 7-6 두 Power 객체를 더하는 += 연산자 작 성 21 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); Power operator+= (Power op2); // += 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power Power::operator+=(Power op2) { kick = kick + op2.kick; // kick 더하기 punch = punch + op2.punch; // punch 더하기 return *this; // 합한 결과 리턴 } int main() { Power a(3,5), b(4,6), c; a.show(); b.show(); c = a += b; // 파워 객체 더하기 a.show(); c.show(); } kick=3,punch=5 kick=4,punch=6 kick=7,punch=11 a, b 출력 a+=b 후 a, c 출력 += 연산자 멤버 함수 구현 operator+=() 멤버 함수 호출
+ 연산자 작성 ( 실습 ): b = a + 2; 22 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); Power operator+ (int op2); // + 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power Power::operator+(int op2) { Power tmp; // 임시 객체 생성 tmp.kick = kick + op2; // kick 에 op2 더하기 tmp.punch = punch + op2; // punch 에 op2 더하기 return tmp; // 임시 객체 리턴 } int main() { Power a(3,5), b; a.show(); b.show(); b = a + 2; // 파워 객체와 정수 더하기 a.show(); b.show(); } kick=3,punch=5 kick=0,punch=0 kick=3,punch=5 kick=5,punch=7 a, b 출력 b = a + 2 후 a, b 출력 + 연산자 멤버 함수 구현 operator+(int) 함수 호출
멤버 함수로 단항 연산자 구현 23
단항 연산자 중복 단항 연산자 피연산자가 하나 뿐인 연산자 연산자 중복 방식은 이항 연산자의 경우와 거의 유사함 단항 연산자 종류 전위 연산자 (prefix operator) !op, ~op, ++op, --op 후위 연산자 (postfix operator) op++, op-- 24
전위 ++ 연산자 중복 25 class Power { public: Power operator++ ( ); }; ++a a. ++ ( ) 컴파일러에 의한 변환 Power Power::operator++( ) { // kick 과 punch 는 a 의 멤버 kick++; punch++; return *this; // 변경된 객체 자신 ( 객체 a) 리턴 } Power a 리턴 타입 매개 변수 없음 전위 ++ 연산자 함수 코드 x = ++a;
예제 7-8 전위 ++ 연산자 작성 26 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); Power operator++ (); // 전위 ++ 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power Power::operator++() { kick++; punch++; return *this; // 변경된 객체 자신 ( 객체 a) 리턴 } int main() { Power a(3,5), b; a.show(); b.show(); b = ++a; // 전위 ++ 연산자 사용 a.show(); b.show(); } kick=3,punch=5 kick=0,punch=0 kick=4,punch=6 a, b 출력 b = ++a 후 a, b 출력 전위 ++ 연산자 멤버 함수 구현 operator++() 함수 호출
예제 7-9( 실습 ) Power 클래스에 ! 연산자 작성 27 ! 연산자를 Power 클래스의 멤버 함수로 작성하라. !a 는 a 의 kick, punch 파워가 모두 0 이면 true, 아니면 false 를 리턴한다. #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); bool operator! (); // ! 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } bool Power::operator!() { if(kick == 0 && punch == 0) return true; else return false; } int main() { Power a(0,0), b(5,5); if(!a) cout << "a 의 파워가 0 이다.“ << endl; // ! 연산자 호출 else cout << "a 의 파워가 0 이 아니다." << endl; if(!b) cout << "b 의 파워가 0 이다." << endl; // ! 연산자 호출 else cout << "b 의 파워가 0 이 아니다." << endl; } a 의 파워가 0 이다. b 의 파워가 0 이 아니다. ! 연산자 멤버 함수 구현 operator!() 함수 호출
후위 연산자 중복, ++ 연산자 28 Power Power::operator++(int x) { Power tmp = *this; // 증가 이전 객체 상태 저장 kick++; punch++; return tmp; // 증가 이전의 객체 ( 객체 a) 리턴 } class Power { public: Power operator ++ (int x ); }; a++ a. ++ ( 임의의 정수 ) 컴파일러에 의한 변환 객체 a 리턴 타입 매개 변수 후위 ++ 연산자 함수 코드
예제 7-10 후위 ++ 연산자 작성 29 ##include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); Power operator++ (int x); // 후위 ++ 연산자 함수 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power Power::operator++(int x) { Power tmp = *this; // 증가 이전 객체 상태를 저장 kick++; punch++; return tmp; // 증가 이전 객체 상태 리턴 } int main() { Power a(3,5), b; a.show(); b.show(); b = a++; // 후위 ++ 연산자 사용 a.show(); // a 의 파워는 1 증가됨 b.show(); // b 는 a 가 증가되기 이전 상태를 가짐 } kick=3,punch=5 kick=0,punch=0 kick=4,punch=6 kick=3,punch=5 a, b 출력 b = a++ 후 a, b 출력 후위 ++ 연산자 멤버 함수 구현 operator++(int) 함수 호출
2 + a 덧셈을 위한 + 연산자 함수 작성 30 c = 2. + ( a ); b = 2 + a; c = + ( 2, a ); 변환 불가능 변환 가능 외부 연산자 함수명 왼쪽 피연산자 오른쪽 피연산자 Power a(3,4), b; b = 2 + a; Power operator+ (int op1, Power op2) { Power tmp; tmp.kick = op1 + op2.kick; tmp.punch = op1 + op2.punch; return tmp; } b = 2 + a;b = + ( 2, a ); 리턴 타입 매개변수 컴파일러에 의한 변환 객체가 아님 ! 멤버함수로 불 가
예제 a 를 위한 + 연산자 함수를 프렌드로 작성 31 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); friend Power operator+(int op1, Power op2); // 프렌드 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power operator+(int op1, Power op2) { Power tmp; // 임시 객체 생성 tmp.kick = op1 + op2.kick; // kick 더하기 tmp.punch = op1 + op2.punch; // punch 더하기 return tmp; // 임시 객체 리턴 } kick=3,punch=5 kick=0,punch=0 kick=3,punch=5 kick=5,punch=7 int main() { Power a(3,5), b; a.show(); b.show(); b = 2 + a; // 파워 객체 더하기 연산 a.show(); b.show(); } + 연산자 함수를 외부 함수로 구현 private 속성인 kick, punch 를 접근하도록 하기 위해, 연산자 함수를 friend 로 선언해야 함 a, b 출력 b = 2+a 후 a, b 출력 operator+(2, a) 함수 호출
+ 연산자를 외부 함수로 구현 32 Power operator+ (Power op1, Power op2) { Power tmp; tmp.kick = op1.kick + op2.kick; tmp.punch = op1.punch + op2.punch; return tmp; } c = a + b; c = + ( a, b ); 리턴 타입 매개변수 컴파일러에 의한 변환
예제 7-12 a+b 를 위한 연산자 함수를 프렌드로 작 성 33 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); friend Power operator+(Power op1, Power op2); // 프렌드 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power operator+(Power op1, Power op2) { Power tmp; // 임시 객체 생성 tmp.kick = op1.kick + op2.kick; // kick 더하기 tmp.punch = op1.punch + op2.punch; // punch 더하기 return tmp; // 임시 객체 리턴 } int main() { Power a(3,5), b(4,6), c; c = a + b; // 파워 객체 + 연산 a.show(); b.show(); c.show(); } kick=3,punch=5 kick=4,punch=6 kick=7,punch=11 + 연산자 함수 구현 객체 a, b, c 순 으로 출력 operator+(a,b) 함수 호출
단항 연산자 ++ 를 프렌드로 작성하기 34 ++a++ ( a ) a++++ ( a, 0 ) Power operator++ (Power& op) { op.kick++; op.punch++; return op; } Power operator++ (Power& op, int x) { Power tmp = op; op.kick++; op.punch++; return tmp; } 컴파일러에 의한 변환 0 은 의미 없는 값으로 전위 연산자와 구분 하기 위함 (a) 전위 연산자 (b) 후위 연산자 리턴 타입
35 예제 연산자를 프렌드로 작성한 예 #include using namespace std; class Power { int kick; int punch; public: Power(int kick=0, int punch=0) { this->kick = kick; this->punch = punch; } void show(); friend Power operator++(Power& op); // 전위 ++ 연산자 함수 프렌드 선언 friend Power operator++(Power& op, int x); // 후위 ++ 연산자 함수 프렌드 선언 }; void Power::show() { cout << "kick=" << kick << ',' << "punch=" << punch << endl; } Power operator++(Power& op) { // 전위 ++ 연산자 함수 구현 op.kick++; op.punch++; return op; // 연산 결과 리턴 } Power operator++(Power& op, int x) { // 후위 ++ 연산자 함수 구현 Power tmp = op; // 변경하기 전의 op 상태 저장 op.kick++; op.punch++; return tmp; // 변경 이전의 op 리턴 } int main() { Power a(3,5), b; b = ++a; // 전위 ++ 연산자 a.show(); b.show(); b = a++; // 후위 ++ 연산자 a.show(); b.show(); } kick=4,punch=6 kick=5,punch=7 kick=4,punch=6 b = ++a 실행 후 a, b 출력 b = a++ 실행 후 a, b 출력 참조 매개 변수 사 용에 주목
실습문제 (7 장 ) p.356 Book 클래스가 다음과 같이 정의된다. class Book { string title; int price; int pages; public: Book(string title="", int price=0, int pages=0) { this->title = title; this->price = price; this->pages = pages; } void show() { cout << title << ' ' << price << " 원 " <<pages << " 페이지 " << endl; } string getTitle() { return title; } };
Book 객체에 대해 다음 연산을 수행하고자 한 다. int main() { Book a(" 청춘 ", 20000, 300); Book b(" 미래 ", 30000, 500); a += 500; // 책 a 의 가격 500 원 증가 b -= 500; // 책 b 의 가격 500 원 감소 a.show(); b.show(); }
+=, -= 연산자 함수를 Book 클래스의 멤버함 수로 구현하시오. +=, -= 연산자 함수를 Book 클래스의 외부함 수로 구현하시오.
2. Book 객체를 활용하는 사례이다. int main() { Book a(" 명품 C++", 30000, 500), b(" 고품 C++", 30000, 500); if(a == 30000) cout << " 정가 원 " << endl; // price 비교 if(a == " 명품 C++") cout << " 명품 C++ 입니다." << endl; // 책 title 비교 if(a == b) cout << " 두 책이 같은 책입니다." << endl; // title, price, pages 모두 비교 }
세 개의 == 연산자 함수를 가진 Book 클래스 를 작성하시오. ( 멤버함수 이용 ) 세 개의 == 연산자 함수를 프렌드 함수로 작성 하시오.
3. 다음 연산을 통해 공짜 책인지 판별하는 ! 연산 자 함수를 작성하시오. int main() { Book book(" 벼룩시장 ", 0, 50); // 가격은 0 if(!book) cout << " 공짜다 " << endl; }
4. 다음 연산을 통해 책의 제목을 사전순으로 비 교하고자 한다. < 연산자 함수를 작성하라. int main() { Book a(" 청춘 ", 20000, 300); string b; cout >"; getline(cin, b); if(b < a) cout << a.getTitle() << " 이 " << b << " 보다 뒤에 있구 나 !" << endl; }
7. Circle 클래스의 전증가 ++, 후증가 ++ class Circle { int radius; public: Circle(int radius = 0) { this->radius = radius; } void show() { cout << "radius = " << radius << " 인 원 " << endl; } };
다음 연산이 가능하게 연산자 ++ 를 프렌드로 작성하시오. int main() { Circle a(5), b(4); ++a; // 반지름을 1 증가 시킨다. b = a++; // 반지름을 1 증가 시킨다. a.show(); b.show(); }
8. Circle 클래스에 정수 + 객체 int main() { Circle a(5), b(4); b = 1 + a; // b 의 반지름을 a 의 반지름에 1 을 더한 것으로 변경 a.show(); b.show(); }