<4D F736F F F696E74202D2038C0E55FB0ADC0C7B3EBC6AE28B0A1BBF3C7D4BCF6BFCD20B4D9C7FCBCBA29>

Similar documents
C++ Programming

Microsoft PowerPoint - additional08.ppt [호환 모드]

JAVA PROGRAMMING 실습 08.다형성

gnu-lee-oop-kor-lec06-3-chap7

PowerPoint Presentation

윤성우의 열혈 TCP/IP 소켓 프로그래밍

1. auto_ptr 다음프로그램의문제점은무엇인가? void func(void) int *p = new int; cout << " 양수입력 : "; cin >> *p; if (*p <= 0) cout << " 양수를입력해야합니다 " << endl; return; 동적할

설계란 무엇인가?

PowerPoint Template

C++ Programming

쉽게 풀어쓴 C 프로그래밍

쉽게 풀어쓴 C 프로그래밍

PowerPoint Presentation

Microsoft PowerPoint - chap06-2pointer.ppt

PowerPoint Template

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

<4D F736F F F696E74202D2037C1D65FB0ADC0C7B3EBC6AE2837C0E55FBBF3BCD329>

PowerPoint Template

슬라이드 1

Microsoft PowerPoint - additional07.ppt [호환 모드]

슬라이드 1

PowerPoint Presentation

Slide 1

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

<4D F736F F F696E74202D2036C0CFC2B05FB0B4C3BCC1F6C7E2C7C1B7CEB1D7B7A1B9D62E707074>

Microsoft PowerPoint - Chapter 6.ppt

1. 객체의생성과대입 int 형변수 : 선언과동시에초기화하는방법 (C++) int a = 3; int a(3); // 기본타입역시클래스와같이처리가능 객체의생성 ( 복습 ) class CPoint private : int x, y; public : CPoint(int a

Slide 1

Microsoft PowerPoint - C++ 5 .pptx

1. 상속의기본개념 다음과같은문제를위한클래스설계 자동차 속성 : 색상, 배기량, 현재속도 메서드 : 가속하라, 멈춰라, 시동을켜라 트럭 속성 : 색상, 배기량, 현재속도, 최대중량 메서드 : 가속하라, 멈춰라, 시동을켜라 택시 속성 : 색상, 배기량, 현재속도, 요금,

1. 기본형의형변환복습 C/C++ 형변환의종류 자동형변환 ( 묵시적형변환 ) 강제형변환 ( 명시적형변환 ) 기본형의자동형변환의예 1. 배열 to 포인터변환 배열명은해당배열의첫번째원소의 주소로변환됨 int ary[10]; int *pary = ary; 2. 함수 to 포

윤성우의 열혈 TCP/IP 소켓 프로그래밍

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

Chapter 6 Objects and Classes

윤성우의 열혈 TCP/IP 소켓 프로그래밍

Microsoft PowerPoint - 8ÀÏ°_Æ÷ÀÎÅÍ.ppt

C++ Programming

(Microsoft PowerPoint - 07\300\345.ppt [\310\243\310\257 \270\360\265\345])

금오공대 컴퓨터공학전공 강의자료

Microsoft PowerPoint - additional06.ppt [호환 모드]

Microsoft PowerPoint - 2강

4 장클래스와객체 클래스와객체 public과 private 구조체와클래스객체의생성과생성자객체의소멸과소멸자생성자와소멸자의호출순서디폴트생성자와디폴트소멸자멤버초기화멤버함수의외부정의멤버함수의인라인함수선언 C++ 프로그래밍입문

1. 클래스와배열 int 형배열선언및초기화 int ary[5] = 1, 2, 3, 4, 5 ; for (int i = 0; i < 5; i++) cout << "ary[" << i << "] = " << ary[i] << endl; 5 장클래스의활용 1

JAVA PROGRAMMING 실습 05. 객체의 활용

윤성우의 열혈 TCP/IP 소켓 프로그래밍

윤성우의 열혈 TCP/IP 소켓 프로그래밍

8. 클래스 D는클래스 A, 클래스 B, 클래스 C로부터상속받아맊들고싶다. 아래빈칸을채우시오. ( 대소문자주의하시오 ) class D { ; Student s; 11. 다음프로그램의실행결과는? 9. 다음프로그램의실행결과는? class A{ A(){cout << " 생성

chap10.PDF

Microsoft PowerPoint - additional01.ppt [호환 모드]

Design Issues

PowerPoint Presentation

쉽게 풀어쓴 C 프로그래밍

No Slide Title

<4D F736F F F696E74202D20C1A63038C0E520C5ACB7A1BDBABFCD20B0B4C3BC4928B0ADC0C729205BC8A3C8AF20B8F0B5E55D>

C++ Programming

17장 클래스와 메소드

설계란 무엇인가?

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

No Slide Title

Microsoft PowerPoint - chap11-포인터의활용.pptx

Microsoft PowerPoint 장강의노트.ppt

윤성우의 열혈 TCP/IP 소켓 프로그래밍

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2

PowerPoint Presentation

Microsoft PowerPoint - ch07 - 포인터 pm0415

11장 포인터

Microsoft PowerPoint - additional03.ppt [호환 모드]

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

금오공대 컴퓨터공학전공 강의자료

PowerPoint 프레젠테이션

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

Microsoft PowerPoint - chap10-함수의활용.pptx

PowerPoint Presentation

Microsoft PowerPoint - [2009] 02.pptx

(Microsoft Word - \301\337\260\243\260\355\273\347.docx)

쉽게 풀어쓴 C 프로그래밍

C++ 기본문법 정리

제11장 프로세스와 쓰레드

Microsoft PowerPoint - Chap12-OOP.ppt

Slide 1

Microsoft PowerPoint - chap11

10.0pt1height.7depth.3width±â10.0pt1height.7depth.3widthÃÊ10.0pt1height.7depth.3widthÅë10.0pt1height.7depth.3width°è10.0pt1height.7depth.3widthÇÁ10.0pt1height.7depth.3width·Î10.0pt1height.7depth.3width±×10.0pt1height.7depth.3width·¡10.0pt1height.7depth.3width¹Ö pt1height.7depth.3widthŬ10.0pt1height.7depth.3width·¡10.0pt1height.7depth.3width½º, 10.0pt1height.7depth.3width°´10.0pt1height.7depth.3widthü, 10.0pt1height.7depth.3widthº¯10.0pt1height.7depth.3width¼ö, 10.0pt1height.7depth.3width¸Þ10.0pt1height.7depth.3width¼Ò10.0pt1height.7depth.3widthµå

KNK_C_05_Pointers_Arrays_structures_summary_v02

Microsoft PowerPoint - java2 [호환 모드]

Microsoft PowerPoint - CSharp-10-예외처리

PowerPoint 프레젠테이션

(8) getpi() 함수는정적함수이므로 main() 에서호출할수있다. (9) class Circle private double radius; static final double PI= ; // PI 이름으로 로초기화된정적상수 public

(Microsoft PowerPoint - java2-lecture3.ppt [\310\243\310\257 \270\360\265\345])

JAVA PROGRAMMING 실습 07. 상속

쉽게 풀어쓴 C 프로그래밍

Microsoft PowerPoint - 5주_강의노트

class Sale void makelineitem(productspecification* spec, int qty) SalesLineItem* sl = new SalesLineItem(spec, qty); ; 2. 아래의액티비티다이어그램을보고 Java 또는 C ++,

JAVA PROGRAMMING 실습 02. 표준 입출력

C++ 기본문법 정리

PowerPoint 프레젠테이션

Microsoft Word - FunctionCall

(Microsoft PowerPoint - 11\300\345.ppt [\310\243\310\257 \270\360\265\345])

Microsoft PowerPoint - chap03-변수와데이터형.pptx

학습목차 2.1 다차원배열이란 차원배열의주소와값의참조

Microsoft PowerPoint - 6주차.pptx

Transcription:

C++ 프로그래밍 강의노트 #8: 8.1 상속의관계 8.2 상속에의한포인터와레퍼런스관계 8.3 가상함수와동적결합 8.4 순수가상함수와추상클래스 8.5 가상소멸자 8.6 다중상속 8.7 실습문제 2007. 5. 2 담당교수 : 조재수 E-mail: jaesoo27@kut.ac.kr 1 학습내용 8.1 상속의관계 8.2 파생된객체포인터와파생된객체레퍼런스 객체포인터 상속된객체와포인터 상속된객체와레퍼런스 8.3 가상함수와동적결합 (dynamic binding) 오버라이딩 (overriding) 가상함수 (virtual function) 다형성과동적결합 가상함수의동작원리와가상함수테이블 8.4 순수가상함수와추상클래스 8.5 가상소멸자 8.6 다중상속 8.7 실습문제 2/43 1

상속의관계 C++ 언어에서상속이매우중요한특징이지만우리가프로그래밍할때모든경우에대하여상속을적용할필요는없다. 객체지향에서상속이중요한역할을하지만상속을해야하는이유가충분하지않은상황에서의상속은오히려해가될수도있다. 상속의관계 IS-A 관계 Has-A(Have-A) 관계 3/43 IS-A 상속관계 class Regular : public Employee class Temporary : public Employee class Salesman : public Regular A Regular is a Employee." -> 정규직사원은사원이다. "A Temporary is a Employee." -> 임시직사원은사원이다. "A Salesman is a Regular." -> 영업직사원은정규직사원이다. 4/43 2

HAS-A(HAVE-A) 상속관계 class Cudgel // 몽둥이클래스 pulbic: void Swing() cout << " Swing a cudgel! " << endl; ; class Police : public Cudgel // 몽둥이를소유하는경찰 void UseWeapon() Swing(); ; int main() Police pol; pol.useweapon(); 예제와같이몽둥이를소유하고있는경찰을표현하기위해상속을사용하는경우 -> HAS(HAVE)-A 관계의상속이라고한다. 즉, Police has a Cudgel. -> 경찰은몽둥이를소유한다. 5/43 HAS-A(HAVE-A) 상속관계 class Point protected: int x, y; Point(int _x, int _y) : x(_x), y(_y) void ShowData() const cout << " 좌표 : << [ << x << "," <<y << "]" << endl; ; const double PI=3.14159 class Circle : public Point double radius; Circle(int x, int y, double rad) : Point(x,y) radius = rad; double GetArea() const return radius*radius*pi; void ShowData() const Point::ShowData(); // ShowData(); // 어떤현상이일어날까? cout << " 반지름 : << radius<< endl; cout << " 원의넓이 : << GetArea() << endl; ; int main() Circle c1(3, 5, 2.2); c1.showdata(); 실행결과는다음과같다. -------------------------------------------- 좌표 : [3, 5] 반지름 : 2.2 원의넓이 : 15.2053 ---------------- 예제에서 Point::ShowData 로하지않고, ShowData 로하면어떤현상이일어날까? 6/43 3

8.2 파생된객체포인터와파생된객체레퍼런스 객체포인터 객체포인터는그객체가저장되어있는주소값을저장할수있는포인터 기반클래스의객체포인터와파생클래스의객체포인터는일반자료형포인터와는조금다른점이있다. A 클래스의객체포인터 (A* a) 는 A 클래스객체의주소값뿐만아니라 A 클래스를상속하는파생클래스들의객체주소값도저장이가능하다. 7/43 #include "header.h" // 7장에서 Employee, Regular, Temporary, Salesman // 클래스가정의된헤더파일 int main(void) Date date1(1995, 2, 8); Regular reg1(" 권상우 ", " 천안시 ", date1, 200.); Date date2(2000, 3, 1); Temporary temp1(" 장동건 ", " 충청남도 ", date2, 4); temp1.setday(20); Date date3(2004, 3, 1); Salesman sale1(" 김삼순 ", " 제주도 ", date3, 100., 0.1); sale1.setsale(20); Date date4(2005, 3, 1); Employee emp1(" 이효리 "," 서울시 ", date4); Employee *emp_ptr = &emp1; // 기반클래스객체포인터 emp_ptr->displayemployee(); cout << "Employee 월급여 : " << emp_ptr ->GetPay() << endl; cout << endl; Regular *reg_ptr = &reg1; // 파생클래스객체포인터 reg_ptr ->DisplayEmployee(); cout << "Regular 월급여 : " << reg_ptr ->GetPay() << endl; reg_ptr->setsalary(300); cout << "Regular 월급여 ( 수정된 ): " << reg_ptr ->GetPay() << endl; cout << endl; Temporary *temp_ptr = &temp1; // 파생클래스객체포인터 temp_ptr->displayemployee(); cout << "Temporary 월급여 : " << temp_ptr ->GetPay() << endl; temp_ptr->setday(25); cout<<"temporary 월급여 ( 수정된 ): " << temp_ptr - >GetPay() << endl; cout << endl; Salesman* sale_ptr = &sale1; // 파생클래스객체포인터 sale_ptr ->DisplayEmployee(); cout << "Salesman 월급여 : " << sale_ptr ->GetPay() << endl; sale_ptr->setsale(60); cout<<"salesman 월급여 (1차수정된 ): "<<sale_ptr- >GetPay()<<endl; sale_ptr->setsalary(500); cout<<"salesman 월급여 (2차수정된 ): "<<sale_ptr - >GetPay()<<endl; 실행결과는다음과같다. ---------------------------------------- 이름 : 이효리주소 : 서울시입사일자 : 2005.3.1 Employee 월급여 : 0 이름 : 권상우주소 : 천안시입사일자 : 1995.2.8 Regular 월급여 : 200 Regular 월급여 ( 수정된 ):300 이름 : 장동건주소 : 충청남도입사일자 : 2000.3.1 Temporary 월급여 : 80 Temporary 월급여 ( 수정된 ): 100 이름 : 김삼순주소 : 제주도입사일자 : 2004.3.1 Salesman 월급여 : 102 Salesman 월급여 (1차수정된 ): 106 Salesman 월급여 (2차수정된 ): 506 8/43 4

[2] 상속된객체와포인터 #include "header.h" // 7 장에서 Employee, Regular, Temporary, Salesman // 클래스가정의된헤더파일 기반클래스객체포인터가파생클래스객체를저장할수 int main(void) Date date1(1995, 2, 8); Employee *emp_ptr1 = new Employee(" 이효리 ", " 서울시 ", date1); emp_ptr1->displayemployee(); Employee *emp_ptr2 = new Regular(" 권상우 ", " 천안시 ", date1, 200.); emp_ptr2->displayemployee(); // emp_ptr2->setsalary(300); // 컴파일에러발생 Employee *emp_ptr3 = new Salesman(" 김삼순 ", " 제주도 ", date1, 100., 0.1); emp_ptr3->displayemployee(); // emp_ptr3->setsale(0.2); // 컴파일에러 Employee *emp_ptr4 = new Temporary(" 장동건 ", " 충청남도 ", date1, 4); emp_ptr4->displayemployee(); // emp_ptr4->setday(30); // 컴파일에러 있도록동적으로메모리를할당하고그주소를가리킬수있지만, 파생클래스의멤버에는접근할권한이없다. Employee 형의포인터들은 Employee 형의포인터이므로 ( 비록파생클래스객체를가리키고있지만 ) Employee 클래스내에선언된멤버에만접근이가능하다. Employee 타입의포인터 (Employee*) 가가리키는대상은컴파일러입장에서 Employee 객체로보인다. 실제가리키는대상이무엇이건 Employee 객체라고인식하는것이다. 프로그래머의관점에서는 Regular 객체또는 Temporary, Salesman 객체를가리키게끔했으므로가리키는대상이각각의파생객체들임을알고있지만, 컴파일러는이를알지못하기때문에이러한현상이발생 9/43 [2] 상속된객체와포인터 [ 그림 8-1] 상속된객체에대한기반클래스객체포인터의권한관계 10/43 5

[3] 상속된객체와레퍼런스 #include "header.h" // 7 장에서 Employee, Regular, Temporary, Salesman // 클래스가정의된헤더파일 int main(void) Date date1(1995, 2, 8); Salesman sale(" 이효리 ", " 서울시 ", date1,100,0.1); Regular &reg1 = sale; Employee &emp1 = sale; sale.displayemployee(); reg1.displayemployee(); emp1.displayemployee(); Regular reg2(" 장동건 ", " 충청남도 ", date1, 200); Employee &emp2 = reg2; // Salesman &sale2 = reg2; // 첫번째컴파일에러 emp2.displayemployee(); // emp2.setsale(300); // 두번째컴파일에러 상속된객체와포인터와의관계와마찬가지로기반클래스의레퍼런스 (Employee &) 는기반클래스객체 (Employee 객체 ) 뿐만아니라, 기반클래스객체를상속하는파생클래스의객체 (Regular, Temporary, Salesman) 도참조가능하다. 첫번째컴파일에러는상속의포인터와마찬가지로하위클래스의객체형 (Salesman &) 으로상위클래스 (Regular 객체 ) 의객체를참조할수없기때문에컴파일에러가발생한것이다. error C2440: 'initializing' : cannot convert from 'class Regular' to 'class Salesman &' 두번째컴파일에러는기반클래스의레퍼런스는참조하는대상이어떤객체이건, 기반클래스타입내에선언된멤버와기반클래스가상속한멤버변수에만접근이가능한데, 파생클래스의멤버함수에접근할려고해서컴파일에러가발생하였다. error C2039: 'SetSale' : is not a member of 'Employee' 11/43 8.3 가상함수와동적결합 (dynamic binding) 오버라이딩 (overriding) 기반클래스에선언된형태의함수를파생클래스에서다시선언하는것 오버라이딩된함수도객체포인터를이용하여호출이가능하다. [ 그림 8-2] 오버라이딩 (overriding) 의특징 12/43 6

오버라이딩 (overriding) 1: 2: 3: 4: class A 5: 6: 7: void fct() 8: cout<<" 클래스 A 의 fct 함수호출 "<<endl; 9: 10: ; 11: 12: class B : public A 13: 14: 15: void fct() 16: cout<<" 클래스 B 의 fct 함수호출 "<<endl; 17: 18: ; 19: 20: int main(void) 21: 22: B* b=new B; 23: b->fct(); // 클래스 B 의 fct 함수호출 24: 25: A* a=b; 26: a->fct(); // 클래스 A 의 fct 함수호출 27: 28: delete b; 29: 30: 실행결과는다음과같다. ----------------------------------------- 클래스 B 의 fct 함수호출클래스 A 의 fct 함수호출 ----------------------------------------- 13/43 오버라이딩 (overriding) [ 그림 8-3] B 객체포인터로 B 객체에접근하는경우 [ 그림 8-4] A 형포인터로 B 형객체에접근할경우 14/43 7

[2] 가상함수 (virtual function) 1: 2: 3: 4: class A 5: 6: 7: virtual void fct() 8: cout<<" 클래스 A 의 fct 함수호출 "<<endl; 9: 10: ; 11: 12: class B : public A 13: 14: 15: void fct() 16: cout<<" 클래스 B 의 fct 함수호출 "<<endl; 17: 18: ; 19: 20: int main(void) 21: 22: B* b=new B; 23: b->fct(); // 클래스 B 의 fct 함수호출 24: 25: A* a=b; 26: a->fct(); // 클래스 A 의가상함수 fct 함수호출 27: 28: delete b; 29: 30: 실행결과는다음과같다. ---------------- 클래스 B 의 fct 함수호출클래스 B 의 fct 함수호출 -------------------------------------------- 함수 fct 앞에 virtual 예약어를제외하고는앞의예제와같다. 이러한함수를가리켜가상함수 (virtual function) 라부른다. [ 그림 8-5] 가상함수호출 15/43 1: 2: 3: 4: class A 5: 6: 7: virtual void fct() 8: cout<<" 클래스 A의 fct 함수호출 "<<endl; 9: 10: ; 11: 12: class B : public A 13: 14: 15: void fct() // virtual void fct() 16: cout<<" 클래스 B의 fct 함수호출 "<<endl; 17: 18: ; 19: 20: class C : public B 21: 22: 23: void fct() 24: cout<<" 클래스 C의 fct 함수호출 "<<endl; 25: 26: ; 27: 28: 29: int main(void) 30: 31: B* b=new C; 32: b->fct(); // 클래스 C에서오버라이딩된 fct 함수호출 33: b->b::fct(); // 클래스 B에서선언된 fct 함수를호출 34: 35: A* a1=b; 36: a1->fct(); // 클래스 C에서오버라이딩된 fct 함수호출 37: a1->a::fct(); // 클래스 A에서선언된 fct 함수호출 38: 39: A* a2=new B; 40: a2->fct(); 41: 41: A* a3 = new A; 42: a3->fct(); 43: 44: delete b; 45: delete a2; 46: delete a3; 47: 48: 49: 실행결과는다음과같다. -------------------------------------------- 클래스 C 의 fct 함수호출클래스 B 의 fct 함수호출클래스 C 의 fct 함수호출클래스 A 의 fct 함수호출클래스 B 의 fct 함수호출클래스 A 의 fct 함수호출 -------------------------------------------- 16/43 8

[2] 가상함수 [ 그림 8-6] virtual 함수상속의특징 [ 그림 8-7] B 형클래스객체포인터를이용한가상함수호출 17/43 [2] 가상함수 [ 그림 8-8] A 형클래스객체포인터를이용한가상함수호출 A 클래스와 B 클래스에서정의된 fct 함수는호출할수없는것인가? A 클래스와 B 클래스에서정의된 fct 함수를호출하기위해서는 33번째줄과 37번째줄에서한것과같이영역결정자 (::) 를이용하면된다. 40번째줄과 42번째줄처럼자기자신의객체를가리키는포인터인경우는자기자신의가상함수를호출한다. 18/43 9

[3] 다형성과동적결합 C++ 에서의다형성 (polymorphism) 을다음과같이해석할수있다. 객체의정확한데이터형을지정하지않아도그객체의멤버함수를호출할수있는것 같은메시지에대해객체에따라서다르게행동하는현상 ( 또는 ) 하나의문장이다른함수를호출하는것 ( 또는 ) 다른형태의객체가같은것처럼취급되는것 // GetPay() 함수가가상함수로선언 : virtual double GetPay(); Employee* emp_ptr; Regular reg; Temporary temp; Salesman sale; double pay; emp_ptr = pay = emp_ptr->getpay(); // Regualr::GetPay() 함수호출 emp_ptr = &temp; pay = emp_ptr->getpay(); // Temporary::GetPay() 함수호출 emp_ptr = &sale; pay = emp_ptr->getpay(); //Salesman::GetPay() 함수호출 19/43 [3] 다형성과동적결합 객체의정확한데이터형을지정하지않은채 emp_ptr->getpay() 라는코드로다른클래스의멤버함수를실행시킬수있다. GetPay() 라는같은메시지에대해 emp_ptr 포인터변수에저장된객체에따라다른급여액을보여주게된다. 이때 Regular, Temporary, Salesman 클래스의객체는같이취급된다. 가상함수를사용하여다형성 (polymorphism) 을구현할수있으며상속성 (inheritance) 과함께 OOP를구성하는중요한요소가된다. C언어에서는함수를호출할때컴파일러는어떤함수가호출되는지, 또한그함수가메모리상의어떤위치에있는지도정확히알고있다. 컴파일할때함수를호출하는코드는고정된함수의주소로번역된다. 이러한것을정적결합 (static binding 또는 early binding) 이라고한다. C++ 에서도가상함수가아닌멤버함수는이러한정적결합을하게된다. 20/43 10

[3] 다형성과동적결합 C++ 에서가상함수가호출될때, 컴파일러는어떤함수를호출하는지알수없다. emp_ptr->getpay() 코드가 Employee, Regular, Temporary, Salesman 중에어떤클래스의 GetPay() 멤버함수를호출하는것인지컴파일시에는알수없다. emp_ptr라는포인터변수에저장된값은실행시에평가해야만한다. 이러한것을동적결함 (dynamic binding 또는 late binding) 이라고한다. 컴파일러가컴파일시점에서어느클래스에서정의된멤버함수를사용해야할지알수없을때프로그램자체가실행시 (run-time) 에함수호출문을평가하여, 어떤버전의멤버함수를호출할지를결정해야한다. 따라서객체포인터를통하여함수를호출함으로써동적결합을사용하는프로그램에서호출되는문장은실행시간 (run-time) 에평가된다. 동적결합의이점은다음과같다. 각클래스에서로다른기능을하고, 같은함수명을가진함수를개별적으로작성하고이함수를호출할수있다. 이미컴파일된코드의기능을자유롭게변경할수있다. 21/43 1: 2: 3: 4: class A 5: 6: 7: virtual void fct() 8: cout<<" 클래스 A의 fct 함수호출 "<<endl; 9: 10: ; 11: 12: class B : public A 13: 14: 15: void fct() // virtual void fct() 16: cout<<" 클래스 B의 fct 함수호출 "<<endl; 17: 18: ; 19: 20: int main(void) 21: 21: B b; 32: b.fct(); // 정적결합 (statci binding) 33: 34: A* a = new B; 35: a->fct(); // 동적결합 (dynamic binding) 36: 37: delete a; 38: 39: 동적결합의이점은이미컴파일된코드의기능을자유롭게변경할수있다. 즉, 다름프로그래머가작성한클래스라이브러리의소스코드를수정하거나다시컴파일하지않고도그기능을얼마든지확장시킬수있게된다. virtual로지정된가상함수라고해서모두동적결합을하는것은아니다. 옆프로그램과같이데이터형이분명할때는일반함수와마찬가지로정적결합을하게된다. 22/43 11

/* 이예제는가상함수가실행시 (run-time) 발생하는임의의사건에어떻게반응하는가를보여준다. */ #include <cstdlib> class base int i; base(int x) i = x; virtual void func() cout << base 클래스의 func() 가상함수 : "; cout << i << ' n'; ; class derived1 : public base derived1(int x) : base(x) void func() cout << derived1 클래스의 func() 가상함수 : "; cout << i*i << ' n'; ; int main() base *p; derived1 d_ob1(10); derived2 d_ob2(10); int i, j; for(i=0; i<10; i++) j = rand(); if((j%2)) p = &d_ob1; // 홀수이면 d_ob1을사용 else p = &d_ob2; // 짝수이면 d_ob2를사용 p->func(); // 적절한함수를호출 class derived2 : public base derived2(int x) : base(x) void func() cout << "derived2 클래스의 func() 가상함수 : "; cout << i+i << ' n'; ; 23/43 [4] 가상함수의동작원리와가상함수테이블 C++ 에서동적결합은가상함수테이블 (virtual function table, v-table) 을사용하여구현된다. 가상함수테이블이란가상함수를사용하는모든클래스에대하여컴파일러가구축하는가상함수포인터배열이다. 가상함수를사용하는클래스는모두각각하나씩의가상함수테이블을갖는다. 예를들어가상함수를사용하는 Employee, Regular, Temporary, Salesman 클래스는각각하나씩의가상함수테이블을갖는다. 가상함수테이블에는클래스에지정된각각의가상함수에대한하나의함수포인터를저장한다. 24/43 12

[4] 가상함수의동작원리와가상함수테이블 class A int a; virtual void vf1() cout<<" 클래스 A 의 vf1()"<<endl; virtual void vf2() cout<<" 클래스 A 의 vf2()"<<endl; virtual void vf3() cout<<" 클래스 A 의 vf3()"<<endl; ; class C : public B int c; virtual void vf3() cout<<" 클래스 C 의 overriding vf3()"<<endl; void fct5() cout<<" 클래스 C 의 vf5(...)"<<endl; ; int main(void) A* aa=new A(); aa->vf3(); C* cc=new C(); cc->vf3(); class B : public A int b; virtual void vf2() cout<<" 클래스 B 의 overriding vf2()"<<endl; void fct4() cout<<" 클래스 B 의 vf4(...)"<<endl; ; 25/43 [4] 가상함수의동작원리와가상함수테이블 [ 그림 8-9] 각클래스에대한가상함수테이블 26/43 13

[4] 가상함수의동작원리와가상함수테이블 가상함수를사용하는클래스의각객체는해당클래스의가상함수테이블의주소값을저장하는 vptr이라는감춰진가상함수테이블포인터변수를갖고있다. 이경우클래스객체들과가상함수테이블들은그림 8-10과같은구조를갖고있다. 가상함수를가지고있는각각의객체는가상함수테이블을가리키는가상함수테이블포인터 (vptr) 멤버변수를가지고있다. 27/43 [4] 가상함수의동작원리와가상함수테이블 vptr a A 클래스가상함수테이블 0x40000 가상함수위치 ( 번지 ) void A::vf1() 0x 1000 void A::vf2() 0x 1100 void A::vf3() 0x 1200 B 클래스객체 vptr a b B 클래스가상함수테이블 0x50000 가상함수위치 ( 번지 ) void A::vf1() 0x1000 void B::vf2() 0x2000 void A::vf3() 0x1200 void B::vf4() 0x 2100 vptr a b c C 클래스가상함수테이블 0x60000 가상함수위치 ( 번지 ) void A::vf1() 0x1000 void B::vf2() 0x2000 void C::vf3() 0x3000 void B::vf4() 0x 2100 void C::vf5() 0x 3100 [ 그림 8-10] 가상함수테이블과클래스객체구조 28/43 14

[4] 가상함수의동작원리와가상함수테이블 [ 그림 8-11] 가상함수테이블포인터변수를이용한가상함수호출 29/43 [4] 가상함수의동작원리와가상함수테이블 기반클래스에가상함수가사용된다면기반클래스는물론그클래스에서파생되는모든클래스에서는싫든좋든가상함수테이블을갖고있어야한다. 또한이들클래스계층도를구성하는모든클래스의객체는객체마다하나의 vptr 즉, 가상함수테이블포인터변수를갖고있어야한다. 가상함수테이블포인터는 2 또는 4 바이트의메모리를더필요로한다. 이것은메모리를과도하게사용하게만드는원인이된다. 가상함수는가상함수테이블을통하여간접적으로호출되기때문에직접해당멤버함수를호출하는것보다는아무래도시간이좀더소요된다. 다른 OOP 언어와비교한다면, C++ 는필요하지않을때는이러한다형성의과부하를피할수있는장치를마련하고있다. 즉, 클래스에적어도하나이상의멤버함수가 virtual 로선언되어있는경우에만가상메커니즘이작동되는것이다. 가상함수는필요할때만적절하게사용하는것이필요하다. 가상함수를사용할때는언제나이와같은과부하가있다는사실을염두에두고서클래스를작성하는것이절대적으로필요하다. 30/43 15

8.4 순수가상함수와추상클래스 // Employee 클래스정의 class Employee char *name; // 사원이름 char *addr; // 사원주소 Date initday; // 사원입사일자 Employee(); Employee(char* _name, char* _addr, Date &_initday); ~Employee(); virtual double GetPay(void) const; void DisplayEmployee(void) const; ; double Employee::GetPay(void) const return 0.0; // Employee 클래스정의 class Employee char *name; // 사원이름 char *addr; // 사원주소 Date initday; // 사원입사일자 Employee(); Employee(char* _name, char* _addr, Date &_initday); ~Employee(); // 순수가상함수선언 virtual double GetPay(void) const = 0; void DisplayEmployee(void) const; // 사원의정보출력함수 ; 31/43 8.4 순수가상함수와추상클래스 순수한가상함수로선언하기위해서는멤버함수원형뒤에 =0 라고붙이면된다. 순수한가상함수로선언된멤버함수는정의할필요가없다. Employee::GetPay() 함수코드를구현할필요가없다. 순수한가상함수는모든파생클래스에서반드시재정의 (overriding) 되어야하기때문에굳이함수코드를구현할필요가없는것이다. 순수한가상함수는파생클래스에다형성인터페이스를제공한다는의미만가질뿐이며실제로는아무일도하지않는다. 순수한가상함수가선언되어있는클래스는인스턴스즉, 객체를가질수없다. Employee 클래스는순수한가상함수 GetPay() 를갖고있기때문에 Employee 데이터형의어떠한객체도생성할수없다. 이러한클래스를 추상적인클래스 (abstract class)' 라고한다. 객체를생성할수있는클래스를구체적인클래스 (concrete class) 라고한다. 추상적인클래스는객체를생성할수는없지만포인터객체변수는선언하여사용할수있다. 추상적인클래스로부터파생된클래스에서순수한가상함수를재정의 (overriding) 하지않으면그파생클래스도추상적인클래스가되어객체를생성할수없게된다. 32/43 16

// 인터페이스를정의하기위해가상함수를사용한다. class area double dim1, dim2; // 도형의두길이 void setarea(double d1, double d2) dim1 = d1; dim2 = d2; void getdim(double &d1, double &d2) d1 = dim1; d2 = dim2; virtual double getarea() = 0; // 순수가상함수 ; class rectangle : public area double getarea() double d1, d2; getdim(d1, d2); return d1 * d2; ; class triangle : public area double getarea() double d1, d2; getdim(d1, d2); return 0.5 * d1 * d2; ; int main() area *p; rectangle r; triangle t; r.setarea(3.3, 4.5); t.setarea(4.0, 5.0); p = &r; cout << "Rectangle 면적 : " << p->getarea() << endl; p = &t; cout << "Triangle 면적 :: " << p->getarea() << endl; 실행결과는다음과같다. ---------------------------------------------- Rectangle 면적 : 14.85 Triangle 면적 : 10 ----------------------------------- 가상함수를사용하는실용적인예제 도형의높이와폭의두길이를갖는 area라는기초클래스를생성한다. 파생클래스에서이함수를오버라이딩할때그파생클래스가정의한도형의면적을반환한다. 기초클래스의 getarea( ) 선언은인터페이스를결정한다. 실제구현은기초클래스를상속받는클래스에서구현된다. 이예제에서는삼각형과사각형의면적이계산된다. 33/43 연습문제 8-1 객체의길이를밀리미터로표시하는추상클래스인 Length 클래스를정의하여라. 그다음 Length 클래스를상속한 MetricLength와 EnglishLength 클래스를정의하여라. MetricLength 클래스는객체의길이를밀리미터 (millimeters), 센티미터 (centimeters), 미터 (meters) 로표시하는메소드를갖는다. EnglishLength 클래스는객체의길이를인치 (inches), 피트 (feet), 야드 (yards) 로표시하는메소드를갖는다. 34/43 17

8.5 가상소멸자 class A A() cout << "Base class A Constructor" << endl; ~A() cout << "Base class A Destructor" << endl; ; class B : public A B() cout << "Derived class B Constructor" << endl; ~B() cout << "Derived class B Destructor" << endl; ; int main(void) A *base_ptr = new B; delete base_ptr; 위의프로그램을컴파일후실행하면실행결과는다음과같다. -------------------------------------------- Base class A Constructor Derived class B Constructor Base class A Destructor -------------------------------------------- 기반클래스인 A 객체생성자가먼저호출되고, 파생클래스 B의생성자함수가호출됨 소멸되는과정에서 A 객체소멸자만호출되고, B 객체의소멸자함수는호출되지않는결과를볼수있다. 그이유는무엇인가? 이러한문제점을해결하기위해서는기반클래스에 가상소멸자 (virtual destructor)" 를선언하면된다. 가상소멸자로선언하기위해서는소멸자원형앞에 'virtual" 이라는예약어를사용 35/43 8.5 가상소멸자 가상소멸자는언제사용해야하나? 만약클래스에가상함수가있다면그클래스에서파생되는클래스의객체는다형성을사용하기위해기반클래스의객체포인터를통하여조작될수있도록 new 연산자를사용하여동적으로생성될수있을것이다. 따라서가상함수를갖고있는클래스를선언할때는가상소멸자를사용하는것이좋다. 36/43 18

연습문제 8-2 다음프로그램에서생성자함수와소멸자함수가호출되는순서를예상해보자. 원하는대로소멸자함수가호출되었는가? 원하는대로소멸자함수가호출되지않았다면원하는대로소멸자함수가호출되도록수정하라. -------------------------------------------------- class A A() cout << "Base class A Constructor" << endl; ~A() cout << "Base class A Destructor" << endl; ; class B : public A B() cout << "Derived class B Constructor" << endl; ~B() cout << "Derived class B Destructor" << endl; ; class C : public B C() cout << "Derived class C Constructor" << endl; ~C() cout << "Derived class C Destructor" << endl; ; int main(void) A *base_ptr = new C; delete base_ptr; 37/43 다중상속 class A void fct1() cout<<"a::fct1"<<endl; ; class B void fct2() cout<<"b::fct2"<<endl; ; class C : public A, public B void ShowData() fct1(); fct2(); ; int main(void) C c; c.showdata(); class A void fct1() cout<<"a::fct1"<<endl; ; class B void fct1() cout<<"b::fct2"<<endl; ; class C : public A, public B void ShowData() fct1(); // 컴파일에러, A::fct1(); 로변경 fct1(); // 컴파일에러, B::fct1(); ; int main(void) C c; c.showdata(); 38/43 19

가상기반클래스 다중기반클래스들이파생된클래스에의해직접상속될때는잠재된문제가존재한다. 이문제가무엇인지이해하기위해, 다음의클래스계층도를생각해보자. 실제 Base가 Derived3에두번상속된다는의미이다. Base의멤버가 Derived3에의하여참조될때, 이것은모호하게된다. Base의두개의복사본이 Derived3에포함되어있기때문에, Base의멤버를참조하는것은 Derived1을통해서간접적으로상속된 Base를참조하는것인지, 아니면 Derived2를통해서간접적으로상속된 Base를참조하는것인지알수없다. 이모호성을해결하기위하여, C++ 에서는 Base의하나의복사본만이 Derived3 에포함되게한다. 이특성을가상기반클래스 (virtual base class) 라고한다. 39/43 class base int i; ; // base 를가상으로상속받는다. class derived1 : virtual public base int j; ; // 역시 base 클래스를가상으로상속받는다. class derived2 : virtual public base int k; ; class derived3 : public derived1, public derived2 int product() return i*j*k; ; int main() derived3 ob; 만약 derived1 과 derived2 가 base 를가상으로상속받지않는다면, 다음문장은모호하므로컴파일에러가발생할것이다. 어떤컴파일에러인가? ob.i = 10; ob.i = 10; ob.j = 3; ob.k = 5; cout << "Product is " << ob.product() << endl; 40/43 20

실습문제 7장까지은행계좌관리프로그램을 7장과 8장에서배운상속과다형성을적용해보자. 은행에서두가지형태의새로운계좌가있다고하자. 하나는신용등급이높고앞으로좋은거래실적이예상되는고객만을대상으로만들어주는 신용계좌 이다. 이계좌는입금시바로 2% 의이자가추가로더해진다고한다. 또다른하나의계좌는 기부계좌 이다. 이계좌는입금금액의 3% 에해당하는금액이사회에기부되는계좌이다. 기부계좌에는기부된금액의총액에대한정보가존재하여야한다. 그래서현재남아있는잔액정보를조회할때, 기부된총액도출력되어야한다. 이러한두가지계좌를지원하기위해서지금까지의프로그램을 Banking System 3.0으로변경해보자. 추가되는 2개의계좌는기존의 Account 클래스를상속하고, 각계좌의특성에맞게필요한멤버변수와멤버함수를추가하면된다. 41/43 42/43 21

질문 & 답변 Thank You! 수고하셨습니다. 43/43 22