Presentation is loading. Please wait.

Presentation is loading. Please wait.

상속 및 동적바인딩 사용하기 USING INHERITANCE AND DYNAMIC BINDING

Similar presentations


Presentation on theme: "상속 및 동적바인딩 사용하기 USING INHERITANCE AND DYNAMIC BINDING"— Presentation transcript:

1 상속 및 동적바인딩 사용하기 USING INHERITANCE AND DYNAMIC BINDING
Chapter 13 상속 및 동적바인딩 사용하기 USING INHERITANCE AND DYNAMIC BINDING

2 13장에서는… 지금까지는 13장에서는 OOP의 다른 중요한 요소를 공부 자신만의 데이터 타입을 작성하는 방법을 공부
상속 (inheritance) 동적 바인딩 (dynamic binding)

3 13.1 상속 (inheritance) 이런 문제 시나리오가 발생한다면… 어떻게 할까?
대학원 학생의 성적도 처리하자. 그런데 대학원생은 논문 (thesis) 점수가 있다. (비슷한데 다르다.) 어떻게 할까? 방법1: Grad라는 class를 완전히 새로 작성 방법2: 지금까지 애써 작성해놓은 Student_info가 있으니 그걸 활용 (이 경우 소프트웨어 재사용성(reuse)이 높다고 말한다.) 13장에서는 방법2를 위한 상속을 공부한다.

4 is-a 관계와 has-a 관계 is-a 관계와 has-a 관계 has-a 관계는 포함 관계
직사각형은 is-a 도형, 잠자리는 is-a 곤충… 의자는 has-a 다리, 선풍기는 has-a 모터, Student는 has-a 중간점수 has-a 관계는 포함 관계 class leg { private: public: }; // chair는 leg을 포함한다 class chair { private: leg l1, l2, l3, l4; public: }; 4

5 상속 is-a 관계는 상속 관계 class diagram { public: … private: };
// rectangle은 diagram이 가진 것을 상속 받는다. class rectangle: public diagram { public: private: }; 5

6 학생성적처리에서 상속 관계 Student (Core라 이름을 바꾸자)와 Grad는 is-a 관계 class Core {
public: Core(); Core(std::istream&); std::string name() const; std::istream& read(std::istream&); double grade() const; private: std::istream& read_common(std::istream&); std::string n; double midterm, final; std::vector<double> homework; }; Grad는 Core로부터 파생 (derived from) Grad는 Core로부터 상속받음 (inherits from) Grad는 파생 클래스, Core는 기본 클래스 class Grad: public Core { public: Grad(); Grad(std::istream&); double grade() const; std::istream& read(std::istream&); private: double thesis; }; 6

7 상속받는 것과 재정의 Core의 모든 멤버는 Grade의 멤버가 된다. Grade 클래스는 자신만의 멤버를 추가할 수 있다.
Grad는 Core로부터 상속 받았으므로, Core의 모든 멤버는 Grade의 멤버가 된다. 단, 생성자, 대입연산자, 소멸자 등은 제외된다 n, midterm, final, homework도 Grad의 멤버 name()과 read_common()도 Grad의 멤버 Grade 클래스는 자신만의 멤버를 추가할 수 있다. thesis 멤버 기본 클래스의 멤버를 재정의: read()와 grade()는 부모 것을 쓰지 않고 자신의 것을 사용 (왜?) 7

8 13.1.1 보호정책 다시보기 Grad의 멤버 함수가 Core의 private 멤버에 접근할 수 있는가? NO!!
Core의 private 멤버: n, midterm, final, homework, read_common() Grad는 접근해야 한다. 따라서… Core는 Grad (자식)에게는 허용하지만 그 외 class는 여전히 차단해야 한다. protected : 파생 클래스가 기본클래스의 protected 멤버들을 접근할 수 있음 8

9 protected Core를 다시 써 보면, class Core { public: Core();
Core(std::istream&); std::string name() const; std::istream& read(std::istream&); double grade() const; protected: std::istream& read_common(std::istream&); double midterm, final; std::vector<double> homework; private: std::string n; }; // 이름을 나타내는 n은 왜 여전히 private에 두나? 9

10 13.1.2 연산 string Core::name() const { return n; }
double Core::grade() const { return ::grade(midterm, final, homework); } istream& Core::read_common(istream& in) // 학생의 이름과 성적을 읽어 저장 in >> n >> midterm >> final; return in; istream& Core::read(istream& in) read_common(in); read_hw(in, homework); 10

11 13.1.2 연산 istream& Grad::read(istream& in) {
Core::read_common(in); // 빨간색 부분은 생략해도 좋다. in >> thesis; read_hw(in, Core::homework); return in; } double Grad::grade() const // 대학원생은 계산된 점수와 논문 (thesis) 점수 중 작은 것을 취한다 return min(Core::grade(), thesis); 11

12 13.1.3 상속과 생성자 파생 타입의 객체를 어떻게 생성할까? 파생 객체는 아래와 같은 과정으로 생성된다
즉 Grad g; 또는 Grad g(cin)과 같이 g라는 객체를 생성할 때 무슨 일이 벌어질까? 자신의 것과 부모 (기본) 클래스 것을 가지게 되는데… 파생 객체는 아래와 같은 과정으로 생성된다 1) 전체 객체 (파생멤버+기본멤버)에 대한 공간 할당 2) 기본 클래스의 생성자 실행: 기본 클래스에 해당하는 부분을 초기화 3) 파생클래스의 생성자 실행: 생성자 초기 설정자(constructor initializer )에 표시된 파생 클래스의 멤버들을 초기화. 만약 있다면, 파생 클래스 생성자의 본체 실행 생성자 초기 설정자: Grad(): thesis(0) { }에서 thesis(0) 부분을 말한다. 12

13 13.1.3 상속과 생성자 생성자 함수들 class Core { public:
Core(): midterm(0), final(0) { } // 디폴트 생성자 Core(std::istream& is) { read(is); } // 인자가 있는 생성자 }; class Grad: public Core { Grad(): thesis(0) { } Grad(std::istream& is) { read(is); } 실행되는 생성자는? Grad g; Grad g(cin); 13

14 13.2 다형성 및 가상 함수 비멤버 함수인 compare()를 생각해 봅시다. 아래와 같이 하면 어떤 일이?
Grad g(cin); // Grade 레코드를 읽는다 Grad g2(cin); // ” Core c(cin); // Core 레코드를 읽는다 Core c2(cin); // ” compare(g, g2); // 파생 클래스를 전달 가능한가? compare(c, c2); compare(g, c); // 심지어 파생과 기본이 섞여 있으면? bool compare(const Core& c1, const Core& c2) { return c1.name() < c2.name(); } const Core& c1=g; const Core& c2=g2; const Core& c1=c; const Core& c2=c2; const Core& c1=g; const Core& c2=c; 14

15 13.2 다형성 및 가상 함수 가능하다. Grad는 Core 파트를 가지고 있으므로, compare()의 레퍼런스 매개변수를 Grad 객체의 Core 파트에 바인딩할 수 있다. (바인딩이란 어떤 것을 다른 것에 연결시키거나 한정시키는 작업) n midterm final homework thesis Core 파트 Grad 객체 15

16 13.2.1 객체의 타입을 모르는 상태에서 값 얻기 점수로 정렬한다고 하면… 이렇게 하면 될까? 그렇지 않다!!
compare()와 엄청난 차이가 있다. Core와 Grad에 모두 grade()함수가 정의되어 있다. Core::grade() Grad::grade() Grad객체에 대해서는 Grad::grade()를 호출하고, Core객체에 대해서는 Core::grade()를 호출해야 한다. bool compare_grades( const Core& c1, const Core& c2) { return c1.grade() < c2.grade(); } 16

17 virtual 함수 제대로 하려면 가상(virtual) 함수로 해야 한다.
이렇게 하면 Core의 grade()를 쓸 건지, 아니면 Grad의 grade()를 쓸 건지는 실행시간(run time)에 결정: 동적 바인딩 virtual은 기본 클래스의 선언부에서만 한다. class Core{ Public: virtual double grade() const; }; 17

18 13.2.2 동적 바인딩 (dynamic binding)
동적 바인딩: 함수가 실행시간에 바인딩 된다 레퍼런스나 포인터를 통해 virtual 함수를 호출하면, 그 함수는 동적 바인딩 된다 정적 바인딩(static binding): 객체 자체에 대한 virtual 함수를 호출하면 정확한 타입이 컴파일 할 때 결정된다 예를 들어, 아래처럼 하면 정적 바인딩이 된다 Grad 객체를 전달할 수는 있는데, 이때 기본 파트만 잘라서 전달 bool compare_grades(const Core c1, const Core c2) { return c1.grade() < c2.grade(); } 18

19 다형성 (polymorphism) 다형성: 동적인 상황에 따라 여러 모양을 띨 수 있는 성질
보충 설명 하자면, C++는 virtual 함수의 동적 바인딩 속성을 통해 다형성을 지원한다 포인터나 레퍼런스를 통해 virtual 함수를 호출한다는 것은, 다형성을 띤 함수 호출을 수행하는 것이다. Core c; Grad g; Core *p; Core& r=g; c.grade(); // Core::grad()에 정적 바인딩 g.grade(); // Core::grad()에 정적 바인딩 p->grade(); // 동적 바인딩. p가 가르키는 객체의 타입에 따라 결정 // 앞부분에서 p=&c; 또는 p=&g에 따라 다르게 동작 r.grade(); // 동적 바인딩. r이 참조하는 객체의 타입에 따라 결정 19

20 13.2.3 요약 class Core { public: Core(): midterm(0), final(0) { }
요약 지금까지 한 것을 정리하면… class Core { public: Core(): midterm(0), final(0) { } Core(std::istream& is) { read(is); } std::string name() const; // as defined in virtual std::istream& read(std::istream&); virtual double grade() const; protected: // accessible to derived classes std::istream& read_common(std::istream&); double midterm, final; std::vector<double> homework; private: // accessible only to Core std::string n; }; 20

21 13.2.3 요약 class Grad: public Core { public: Grad(): thesis(0) { }
Grad(std::istream& is) { read(is); } // as defined in // Note: grade and read are virtual by inheritance double grade() const; std::istream& read(std::istream&); private: double thesis; }; bool compare(const Core&, const Core&); 21

22 13.3 상속을 통해 문제 해결하기 main()을 아래와 같이 작성하면… 대학원생은? int main() {
vector<Core> students; // read and process Core records Core record; string::size_type maxlen = 0; // read and store the data while (record.read(cin)) { // Core에서 읽어들인다. maxlen = max(maxlen, record.name().size()); students.push_back(record); } sort(students.begin(), students.end(), compare); for (vector<Core>::size_type i=0; i!=students.size(); ++i) { cout << students[i].name() <<string(maxlen+1-students[i].name().size(), ' '); try { double final_grade = students[i].grade(); // Core의 성적계산 streamsize prec = cout.precision(); cout << setprecision(3) << final_grade << setprecision(prec) << endl; } catch (domain_error e) { cout << e.what() << endl; return 0; 22

23 그렇다고 아래와 같이 작성하면… 학부생은? int main() {
vector<Grad> students; // read and process Core records Grad record; string::size_type maxlen = 0; // read and store the data while (record.read(cin)) { // Grad에서 읽어들인다. maxlen = max(maxlen, record.name().size()); students.push_back(record); } sort(students.begin(), students.end(), compare); for (vector<Core>::size_type i=0; i!=students.size(); ++i) { cout << students[i].name() <<string(maxlen+1-students[i].name().size(), ' '); try { double final_grade = students[i].grade(); // Grad의 성적 계산 streamsize prec = cout.precision(); cout << setprecision(3) << final_grade << setprecision(prec) << endl; } catch (domain_error e) { cout << e.what() << endl; return 0; 23

24 13.3.1 알지 못하는 타입에 대한 컨테이너 학부생과 대학원생이 섞여 있을 때는 아래와 같이 하면 될까? 안된다! 왜?
알지 못하는 타입에 대한 컨테이너 학부생과 대학원생이 섞여 있을 때는 아래와 같이 하면 될까? int main() { vector<Core*> students; // virtual을 쓰기 위해 포인터로 한다. Core* record; string::size_type maxlen = 0; // read and store the data while(record->read(cin)) { // 타입에 따라 Core 또는 Grad에서.. 제대로 될까?? // 비정상적 종료! maxlen = max(maxlen, record->name().size()); students.push_back(record); } 안된다! 왜? 24

25 포인터의 벡터를 정렬 Core 객체를 가리키는 두 포인터들을 취하는 새로운 비교 함수
bool compare_Core_ptrs(const Core* cp1, const Core* cp2) { return compare(*cp1, *cp2); }

26 가리키는 객체에 따라 적절한 함수 호출 다음과 같이 해야 한다. int main() {
vector<Core*> students; // 객체가 아닌 포인터를 저장 Core* record; // 임시 변수도 포인터 char ch; string::size_type maxlen = 0; // 데이터를 읽고 저장 while (cin >> ch) { if (ch == 'U') record = new Core; // Core 객체 할당 else record = new Grad; // Grad 객체 할당 record->read(cin); // virtual 함수 호출 maxlen = max(maxlen, record->name().size()); students.push_back(record); } // 포인터에 대해 작동하는 compare 전달 sort(students.begin(), students.end(), compare_Core_ptrs); 26

27 객체 해제할 때, 파생클래스의 소멸자 호출? // 이름과 성적을 출력
for (vector<Core*>::size_type i = 0; i != students.size(); ++i) { // students[i]는 포인터 cout << students[i]->name() << string(maxlen students[i]->name().size(), ' '); try { double final_grade = students[i]->grade(); streamsize prec = cout.precision(); cout << setprecision(3) << final_grade << setprecision(prec) << endl; } catch (domain_error e) { cout << e.what() << endl; } delete students[i]; // 읽을 때 할당된 객체를 해제 (문제 없나?) return 0; 27

28 13.3.2 가상 소멸자(virtual destructor)
객체를 할당할 때, Grad와 Core 객체 모두에 대해 공간을 할당했다. 이 객체들의 포인터는 Core* 이다 따라서, delete를 실행하면, 포인터가 Grad의 객체를 가리키고 있다고 해도, Core에 대한 포인터를 해제시킨다.  문제 발생 포인터에 대해 delete를 호출하면, 자동으로 만들어진 소멸자가 실행되고, 그 객체가 사용한 공간을 반환한다. 이때, 시스템은 어떤 소멸자를 실행해야 할까요? Grad의 멤버를 소멸시켜야 할까요? Core의 멤버를 소멸시켜야 할 까요? 실제로 포인터가 가리키고 있는 타입에 따라 소멸해야 한다!!  virtual 소멸자 class Core { public: virtual ~Core() { } // 이전과 동일


Download ppt "상속 및 동적바인딩 사용하기 USING INHERITANCE AND DYNAMIC BINDING"

Similar presentations


Ads by Google