상속의이해 박종혁교수 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr SeoulTech 프로그래밍입문
3 상속의기본개념 상속의예1 " 철수는아버지로부터좋은목소리와큰키를물려받았다." 상속의예2 "Student 클래스가 Person 클래스를상속한다." 아버지 Person 철수 Stduent
4 파생클래스 (derived class) 상속 (inheritance) 한클래스가다른클래스에서정의된속성 ( 자료, 함수 ) 를이어받아그대로사용 이미정의된클래스를바탕으로필요한기능을추가하여정의 소프트웨어재사용지원 파생클래스는 C++ 에서각클래스의속성을공유하고물려받는객체지향프로그래밍의상속 (inheritance) 을구현한것 이미만들어진기존의클래스를베이스클래스 (base class) 또는부모클래스 (parent class) or 슈퍼클래스 이를상속받아새로만들어지는클래스를파생클래스 (derived class) or 하위클래스라고도함.
5 공용부분상속 class 파생클래스명 : public[private] 베이스클래스명 ; public 베이스클래스 파생클래스 전용부분 전용부분 공용부분 상속 공용부분 private 베이스클래스 파생클래스 전용부분공용부분 상속 전용부분공용부분 객체생성순서 ---------------- 1. 메모리할당 2. 베이스클래스생성자실행 3.Derived 클래스생성자실행
6 상속의이해를위한이책의접근방식 위의흐름대로상속을이해하기바랍니다. 상속은 기존에정의해놓은클래스의재활용을목적으로만들어진문법적요소 라고이해하는경우 가많다. 하지만상속에는다른, 더중요한의미가담겨있다.
7 문제의제시 프로그램에추가할직급의형태 확장이후의급여지급방식 이문제는영업직과임시직에해당하는클래스의추가로끝나지않는다. 컨트롤클래스인 EmployeeHandlder 클래스의대대적인변경으로이어진다. 좋은코드는요구사항의변경및기능의추가에따른변경이최소화되어야한다. 그리고이를위한해결책으로 상속이사용된다.
8 EmployeeManager1.cpp #include <iostream> #include <cstring> using namespace std; class PermanentWorker private: char name[100]; int salary; PermanentWorker(char* name, int money) : salary(money) strcpy(this->name, name); int GetPay() const return salary; void ShowSalaryInfo() const cout<<"name: "<<name<<endl; cout<<"salary: "<<GetPay()<<endl<<endl; ; class EmployeeHandler private: PermanentWorker* emplist[50]; int empnum; EmployeeHandler() : empnum(0) void AddEmployee(PermanentWorker* emp) emplist[empnum++]=emp; void ShowAllSalaryInfo() const for(int i=0; i<empnum; i++) emplist[i]->showsalaryinfo(); void ShowTotalSalary() const int sum=0; for(int i=0; i<empnum; i++) sum+=emplist[i]->getpay(); cout<<"salary sum: "<<sum<<endl; ~EmployeeHandler() for(int i=0; i<empnum; i++) delete emplist[i]; ;
9 EmployeeManager1.cpp int main(void) // 직원관리를목적으로설계된컨트롤클래스의객체생성 EmployeeHandler handler; // 직원등록 handler.addemployee(new PermanentWorker("KIM", 1000)); handler.addemployee(new PermanentWorker("LEE", 1500)); handler.addemployee(new PermanentWorker("JUN", 2000)); // 이번달에지불해야할급여의정보 handler.showallsalaryinfo(); // 이번달에지불해야할급여의총합 handler.showtotalsalary(); return 0;
11 상속의방법과그결과 Person 클래스를 public 상속함 Person 클래스의멤버 용어정리
12 상속받은클래스의생성자정의 이니셜라이저를통해서유도클래스는기초클래스의생성자를명시적으로호출해야한다. 유도클래스의생성자는기초클래스의멤버를초기화할의무를갖는다. 단! 기초클래스의생성자를명시적으로호출해서초기화해야한다. 때문에유도클래스 UnivStudent 는기초크래스의생성자호출 을위한인자까지함께전달받아야한다. private 멤버는유도클래스에서도접근이불가능하므로, 생성 자의호출을통해서기초클래스의멤버를초기화해야한다.
13 UnivStudentInheri.cpp #include <iostream> #include <cstring> using namespace std; class Person private: int age; // 나이 char name[50]; // 이름 Person(int myage, char * myname) : age(myage) strcpy(name, myname); void WhatYourName() const cout<<"my name is "<<name<<endl; void HowOldAreYou() const cout<<"i'm "<<age<<" years old"<<endl; ; class UnivStudent : public Person private: char major[50]; // 전공과목 UnivStudent(char * myname, int myage, char * mymajor) : Person(myage, myname) strcpy(major, mymajor); void WhoAreYou() const WhatYourName(); HowOldAreYou(); cout<<"my major is "<<major<<endl<<endl; ; int main(void) UnivStudent ustd1("lee", 22, "Computer eng."); ustd1.whoareyou(); ; UnivStudent ustd2("yoon", 21, "Electronic eng."); ustd2.whoareyou(); return 0;
14 유도클래스의객체생성과정 실행결과 유도클래스의객체생성과정에서기초클래스의생성자는 100% 호출된다. 유도클래스의생성자에서기초클래스의생성자호출을명시하지않으면, 기초클래스의 void 생성자가호출된다.
15 DerivCreOrder.cpp #include <iostream> using namespace std; class SoBase private: int basenum; SoBase() : basenum(20) cout<<"sobase()"<<endl; SoBase(int n) : basenum(n) cout<<"sobase(int n)"<<endl; void ShowBaseData() cout<<basenum<<endl; ; class SoDerived : public SoBase private: int derivnum; SoDerived() : derivnum(30) cout<<"soderived()"<<endl; SoDerived(int n) : derivnum(n) cout<<"soderived(int n)"<<endl; ; SoDerived(int n1, int n2) : SoBase(n1), derivnum(n2) cout<<"soderived(int n1, int n2)"<<endl; void ShowDerivData() ShowBaseData(); cout<<derivnum<<endl; int main(void) cout<<"case1... "<<endl; SoDerived dr1; dr1.showderivdata(); cout<<"-------------------"<<endl; cout<<"case2... "<<endl; SoDerived dr2(12); dr2.showderivdata(); cout<<"-------------------"<<endl; cout<<"case3... "<<endl; SoDerived dr3(23, 24); dr3.showderivdata(); return 0; ;
16 유도클래스의객체생성과정 case1 순서 1. 메모리공간의할당 SoDerived dr1 순서 2. 유도클래스의 void 생성자호출 순서 3. 이니셜라이저를통한기초클래스의생성자호출이명시적으로정의되어있지않으므로 void 생성자호출 순서 4. 유도클래스의실행
17 유도클래스의객체생성과정 case2 순서 1. 메모리공간의할당 SoDerived dr3(23, 24); 순서 2. 유도클래스의생성자호출 순서 3. 기초클래스의생성자호출및실행 순서 4. 유도클래스의생성자실행
18 상속의소멸자예 #include <iostream> using std::endl; using std::cout; class AAA //Base 클래스 AAA() cout<<"aaa() call!"<<endl; ~AAA() cout<< ~AAA(int i) call!"<<endl; ; class BBB : public AAA //Derived 클래스 BBB() cout<<"bbb() call!"<<endl; ~BBB() cout<<"~bbb() call!"<<endl; ; int main(void) BBB bbb; return 0; AAA() call! BBB() call! ~BBB() call! ~AAA() call! 객체소멸순서 ---------------- 1. Derived 클래스소멸자실행 2. base 클래스소멸자실행 3. 메모리반환 ( 해제 )
19 유도클래스객체의소멸과정 실행결과 유도클래스의소멸자가실행된이후에기초클래스의소 멸자가실행된다. 스택에생성된객체의소멸순서는생성순서와반대이다.
20 DerivDestOrder.cpp #include <iostream> using namespace std; class SoBase private: int basenum; SoBase(int n) : basenum(n) cout<<"sobase() : "<<basenum<<endl; ~SoBase() cout<<"~sobase() : "<<basenum<<endl; ; class SoDerived : public SoBase private: int derivnum; SoDerived(int n) : SoBase(n), derivnum(n) cout<<"soderived() : "<<derivnum<<endl; ~SoDerived() cout<<"~soderived() : "<<derivnum<<endl; ; int main(void) SoDerived drv1(15); SoDerived drv2(27); return 0; ;
21 유도클래스정의모델 기초클래스의멤버대상의동적할당은기초클래 스의생성자를통해서, 소멸역시기초클래스의 소멸자를통해서
22 DestModel.cpp #include <iostream> #include <cstring> using namespace std; class Person private: char * name; Person(char * myname) name=new char[strlen(myname)+1]; strcpy(name, myname); ~Person() delete []name; void WhatYourName() const cout<<"my name is "<<name<<endl; ; class UnivStudent : public Person private: char * major; UnivStudent(char * myname, char * mymajor) :Person(myname) major=new char[strlen(mymajor)+1]; strcpy(major, mymajor); ~UnivStudent() delete []major; void WhoAreYou() const WhatYourName(); cout<<"my major is "<<major<<endl<<endl; ; int main(void) UnivStudent st1("kim", "Mathmatics"); st1.whoareyou(); ; UnivStudent st2("hong", "Physics"); st2.whoareyou(); return 0;
24 세가지형태의상속 public 상속 접근제어권한을그대로상속한다! 단, private 은접근불가로상속한다! protected 상속 protected 보다접근의범위가넓은멤버는 protected 로상속한다. 단, private 은접근불가로상속한다! private 상속 private 보다접근의범위가넓은멤버는 protected 로상속한다. 단, private 은접근불가로상속한다!
25 세가지형태의상속 접근권한변경 - Base 클래스의멤버는상속되는과정에서접근권한변경 Base 클래스 상속형태 public 상속 protected 상속 private 상속 public 멤버 public protected private Protected 멤머 protected protected private Private 멤버접근불가접근불가접근불가 class 클래스명 [private:] protected: // 전용부분멤버정의 // 클래스외부접근및상속안됨 // 보호부분멤버정의 // 클래스외부접근안됨, 상속됨 // 공용부분멤버정의 // 클래스외부접근및상속됨 ;
26 세가지형태의상속 class Base private: int a; protected: int b; int c; ; int main(void) Derived object; return 0; ; class Derived : public Base // public 상속 ; // EMPTY protected? private? ** 사용된상속보다넓은범위는그범위로맞춤.
27 protected 로선언된멤버가허용하는접근의범위 private 을기준으로보면, protected 는 private 과달리상속관계에서의접근 을허용한다!
28 protected 멤버상속 파생클래스의멤버함수는베이스클래스의공용멤버에직접접근이가능하지만베이스클래스의전용멤버에대해서는접근할수없음 파생클래스의멤버함수가베이스클래스의전용멤버를상속받아자유로이사용을위해서는다음두가지방식사용 프렌드함수를사용 베이스클래스정의시 protected 키워드를사용한보호부분을정의 보호부분에있는멤버는전용멤버와같이외부에서는직접접근할수없지만파생클래스의멤버함수에서는직접접근이가능 보호부분멤버가파생클래스에서 public 으로상속받으면파생클래스의 protected 멤버가됨 private 으로상속받으면파생클래스에서전용부분멤버가됨
29 protected 멤버상속예 상속관계에놓여있는경우접근허용 그이외는 private 멤버와동일 class AAA private: int a; protected: int b; ; class BBB: public AAA void SetData() a=10; // private 멤버, 따라서에러 b=10; // protected 멤버, OK! ; int main(void) AAA aaa; aaa.a=10; // private 멤버, 따라서에러!! aaa.b=20; // protected 멤버, 따라서에러!! BBB bbb; bbb.setdata(); return 0;
30 protected 상속과 private 상속 private 상속의결과 때문에이이상의상속은무의미할수있다. protected 상속의결과
32 상속의조건 public 상속은 is-a 관계가성립되도록하자. 일반화 ParttimeStd 구체화
33 상속의조건 잘못된상속의예
34 상속의조건 HAS-A( 소유 ) 관계에의한상속! 경찰은몽둥이를소유한다 The Police have a cudgel.
35 /* hasa1.cpp */ #include <iostream> using std::endl; using std::cout; class Cudgel // 몽둥이 void Swing() cout<<"swing a cudgel!"<<endl; ; class Police : public Cudgel // 몽둥이를소유하는경찰 void UseWeapon() Swing(); ; int main() Police pol; pol.useweapon(); return 0; Police is a Cudgel (X) Police has a Cudgel (O)
36 상속의조건 HAS-A에의한상속그리고대안! 포함관계를통해서소유관계를표현 객체멤버에의한포함관계의형성 객체포인터멤버에의한포함관계의형성
37 상속의조건 /* hasa2.cpp */ class Cudgel // 몽둥이 void Swing() cout<<"swing a cudgel!"<<endl; ; class Police // 몽둥이를소유하는경찰 Cudgel cud; void UseWeapon() cud.swing(); ; int main() Police pol; pol.useweapon(); return 0;
38 상속의조건 /* hasa3.cpp */ class Cudgel // 몽둥이 void Swing() cout<<"swing a cudgel!"<<endl; ; class Police // 몽둥이를소유하는경찰 Cudgel* cud; Police() cud=new Cudgel; ~Police() delete cud; void UseWeapon() cud->swing(); ; int main() Police pol; pol.useweapon(); return 0;
39 상속의기본조건인 IS-A 관계의성립 무선전화기는전화기의기본기능에새로운특성이추가된것이다. 노트북컴퓨터는컴퓨터의기본기능에새로운특성이추가된것이다. 이렇듯 is-a 관계는논리적으로상속을기반으로표현하기에매우적절 하다.
40 IS-A 기반의예제 예제는도서본문을참조합시다!
41 ISAInheritance.cpp #include <iostream> #include <cstring> using namespace std; class Computer private: char owner[50]; Computer(char * name) strcpy(owner, name); void Calculate() cout<<" 요청내용을계산합니다."<<endl; ; class NotebookComp : public Computer private: int battary; NotebookComp(char * name, int initchag) : Computer(name), battary(initchag) void Charging() battary+=5; void UseBattary() battary-=1; void MovingCal() if(getbattaryinfo()<1) cout<<" 충전이필요합니다."<<endl; return; cout<<" 이동하면서 "; Calculate(); UseBattary(); int GetBattaryInfo() return battary; ;
42 ISAInheritance.cpp class TabletNotebook : public NotebookComp private: char regstpenmodel[50]; TabletNotebook(char * name, int initchag, char * pen) : NotebookComp(name, initchag) strcpy(regstpenmodel, pen); void Write(char * peninfo) if(getbattaryinfo()<1) cout<<" 충전이필요합니다."<<endl; return; if(strcmp(regstpenmodel, peninfo)!=0) cout<<" 등록된펜이아닙니다."; return; cout<<" 필기내용을처리합니다."<<endl; UseBattary(); ; int main(void) NotebookComp nc(" 이수종 ", 5); TabletNotebook tn(" 정수영 ", 5, "ISE-241-242"); nc.movingcal(); tn.write("ise-241-242"); return 0;
43 HAS-A 관계를상속으로구성하면 경찰은총을소유한다. 경찰 has a 총! has a 관계도상속으로구현이가능하다. 하지만이러한경우 Police 와 Gun 은강한연관성을띠게된다. 따라서총을소유하지않은경찰이나, 다른무기를소유하는경찰을표현하기가쉽지않아진다.
44 HASInheritance.cpp #include <iostream> #include <cstring> using namespace std; class Gun private: int bullet; // 장전된총알의수 Gun(int bnum) : bullet(bnum) void Shut() cout<<"bbang!"<<endl; bullet--; ; class Police : public Gun private: int handcuffs; // 소유한수갑의수 Police(int bnum, int bcuff) : Gun(bnum), handcuffs(bcuff) void PutHandcuff() cout<<"snap!"<<endl; handcuffs--; ; int main(void) Police pman(5, 3); // 총알 5, 수갑 3 pman.shut(); pman.puthandcuff(); return 0;
45 HAS-A 관계는포함으로표현한다 has a의과계를포함의형태로표현하면, 두클래스간연관성은낮아지며, 변경및확장이용이해진다. 즉, 총을소유하지않은경찰의표현이쉬워지고, 추가로무기를소유하는형태로의확장도간단해진다.
46 HASComposite.cpp #include <iostream> #include <cstring> using namespace std; class Gun private: int bullet; // 장전된총알의수 Gun(int bnum) : bullet(bnum) void Shut() cout<<"bbang!"<<endl; bullet--; ; class Police private: int handcuffs; // 소유한수갑의수 Gun * pistol; // 소유하고있는권총 Police(int bnum, int bcuff) : handcuffs(bcuff) if(bnum>0) pistol=new Gun(bnum); else pistol=null; void PutHandcuff() cout<<"snap!"<<endl; handcuffs--; void Shut() if(pistol==null) cout<<"hut BBANG!"<<endl; else pistol->shut(); ~Police() if(pistol!=null) delete pistol; ; int main(void) Police pman1(5, 3); pman1.shut(); pman1.puthandcuff(); Police pman2(0, 3); // 권총소유하지않은경찰 pman2.shut(); pman2.puthandcuff(); return 0;
Q & A