Chapter 06. 상속의이해 박종혁교수 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr SeoulTech 2017-2 nd 프로그래밍입문 (2)
06-1. 상속의기본개념
상속의기본개념 상속 (inheritance) 한클래스가다른클래스에서정의된속성 ( 자료, 함수 ) 를이어받아그대로사용 이미정의된클래스를바탕으로필요한기능을추가하여정의
상속의예1 " 철수는아버지로부터좋은목소리와큰키를물려받았다." 상속의예2 "Student 클래스가 Person 클래스를상속한다." 아버지 Person 철수 Stduent
상속의장점 상속을통하여기존클래스의속성 ( 자료, 함수 ) 재사용 기존클래스의일부변경도가능 상속을이용하게되면복잡한 GUI 프로그램을순식간에작성 상속은이미작성된검증된소프트웨어를재사용 신뢰성있는소프트웨어를손쉽게개발, 유지보수 코드의중복감소
용어정의 베이스클래스 (base class) 기존에이미만들어진클래스 부모클래스 (parent class) or 슈퍼클래스라고도함 파생클래스 (derived class) 이를상속받아새로만들어지는클래스 하위클래스라고도함 파생클래스는 C++ 에서각클래스의속성을공유하고물려받는객체지향프로그래밍의상속 (inheritance) 을구현한것
상속의예
공용부분상속 class 파생클래스명 : public[private] 베이스클래스명 ; public 베이스클래스 파생클래스 전용부분 공용부분 상속 전용부분 공용부분 객체생성순서 1. 메모리할당 2.Base 클래스생성자실행 3.Derived클래스생성자실행 private 베이스클래스파생클래스 전용부분공용부분 상속 전용부분 공용부분
상속의표현예제 1) class Car int speed; class SportsCar : public Car bool turbo; 베이스클래스 파생클래스 상속한다는의미
상속의표현예제 2)
정리 : 상속을이용하면? 1. 간결한클래스작성 기본클래스의기능을물려받아파생클래스를간결하게작성 2. 클래스간의계층적분류및관리의용이함 상속은클래스들의구조적관계파악용이 3. 클래스재사용과확장을통한소프트웨어생산성향상 빠른소프트웨어생산필요 기존에작성한클래스의재사용 상속 상속받아새로운기능을확장 앞으로있을상속에대비한클래스의객체지향적설계필요
06-2. 상속의문법적인이해
상속의예제 Car +speed +gear +color +setgear()() +speedup()() +speeddown()() SportsCar -turbo : bool +setturbo()()
Car 클래스 #include <iostream> #include <string> using namespace std; class Car public: // 3 개의멤버변수선언 int speed; // 속도 int gear; // 주행거리 string color; // 색상 ; // 3 개의멤버함수선언 void setgear(int newgear) // 기어설정멤버함수 gear = newgear; void speedup(int increment) // 속도증가멤버함수 speed += increment; void speeddown(int decrement) // 속도감소멤버함수 speed -= decrement;
SportsCar 클래스 // Car 클래스를상속받아서다음과같이 SportsCar 클래스를작성하여보자. class SportsCar : public Car // Car 를상속받는다. // 1 개의멤버변수를추가 bool turbo; public: ; // 1 개의멤버함수를추가 void setturbo(bool newvalue) // 터보모드설정멤버함수 turbo = newvalue;
SportsCar 클래스 int main() SportsCar c; c.color = "Red"; c.setgear(3); c.speedup(100); c.speeddown(30); c.setturbo(true); return 0; // 부모클래스멤버변수접근 // 부모클래스멤버함수호출 // 부모클래스멤버함수호출 // 부모클래스멤버함수호출 // 자체멤버함수호출 파생클래스는베이스클래스의변수와함수를마치자기것처럼사용할수있다.
상속은왜필요한가? 상속 베이스클래스 파생클래스 중복되는코드는베이스클래스에모은다.
상속계층도 상속은여러단계로이루어질수있다.
상속의방법과그결과 Person 클래스를 public 상속함 Person 클래스의멤버 파생 용어정리
상속에서의객체의생성, 생성자와소멸자순서 파생클래스 (1) 베이스클래스 (2) 파생클래스 (3) 베이스클래스
상속받은클래스의생성자정의 - 이니셜라이저를통해서파생클래스는베이스클래스의생성자를명시적으로호출해야한다. - 파생클래스의생성자는베이스클래스의멤버를초기화할의무를갖는다. 단! 베이스클래스의생성자를명 시적으로호출해서초기화해야한다. - 파생클래스 UnivStudent는베이스클래스의생성자호출을위한인자까지함께전달받아야한다. - private 멤버는파생클래스에서도접근이불가능하므로, 생성자의호출을통해서베이스클래스의멤버를초기화해야한다.
생성자호출관계및실행순서 A() 호출 class A public: A() cout << " 생성자 A" << endl; ~A() cout << " 소멸자 A" << endl; ; A() 실행 리턴 int main() C c; // c 생성 B() 호출 return 0; // c 소멸 C() 호출 class B : public A public: B() cout << " 생성자 B" << endl; ~B() cout << " 소멸자 B" << endl; ; class C : public B public: C() cout << " 생성자 C" << endl; ~C() cout << " 소멸자 C"<< endl; ; B() 실행 리턴 C() 실행 생성자 A 생성자 B 생성자 C 소멸자 C 소멸자 B 소멸자 A 컴파일러는 C() 생성자실행코드를만들때, 생성자 B() 를호출하는코드삽입
파생클래스의객체생성과정예제 // 베이스클래스 // 파생클래스
실행결과?
실행결과
파생클래스의객체생성과정 case1 순서 1. 메모리공간의할당 SoDerived dr1 순서 2. 파생클래스의 void 생성자호출 순서 3. 이니셜라이저를통한기초클래스의생성자호출이명시적으로정의되어있지않으므로 void 생성자호출 순서 4. 파생클래스의실행
파생클래스의객체생성과정 case3 순서 1. 메모리공간의할당 SoDerived dr3(23, 24); 순서 2. 파생클래스의생성자호출 순서 3. 베이스클래스의생성자호출및실행 순서 4. 파생클래스의생성자실행
파생클래스객체의소멸과정 실행결과 파생클래스의소멸자가실행된후베이스클래스의소멸자 가실행된다. 스택에생성된객체의소멸순서는생성순서와반대이다.
상속의소멸자예 #include <iostream> using std::endl; using std::cout; class AAA //Base 클래스 public: AAA() cout<<"aaa() call!"<<endl; ~AAA() cout<< ~AAA(int i) call!"<<endl; ; class BBB : public AAA //Derived 클래스 public: 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. 파생클래스소멸자실행 2. 베이스클래스소멸자실행 3. 메모리반환 ( 해제 )
06-3. 상속의형태및 protected 선언
접근제어지정자
세가지형태의상속 public 상속 접근제어권한을그대로상속한다! 단, private 은접근불가로상속한다! protected 상속 protected보다접근의범위가넓은멤버는 protected로상속한다. 단, private은접근불가로상속한다! private 상속 private보다접근의범위가넓은멤버는 protected로상속한다. 단, private은접근불가로상속한다!
세가지형태의상속 접근권한변경 - Base 클래스의멤버는상속되는과정에서접근권한변경 Base 클래스 상속형태 public 상속 protected 상속 private 상속 public 멤버 public protected private Protected 멤머 protected protected private Private 멤버접근불가접근불가접근불가 class 클래스명 [private:] protected: public: // 전용부분멤버정의 // 클래스외부접근및상속안됨 // 보호부분멤버정의 // 클래스외부접근안됨, 상속됨 // 공용부분멤버정의 // 클래스외부접근및상속됨 ;
세가지형태의상속예 class Base private: int a; protected: int b; public: int c; ; int main(void) Derived object; return 0; ; class Derived : public Base // public 상속 ; // EMPTY protected? private? ** 사용된상속보다넓은범위는그범위로맞춤!!
멤버의접근지정에따른접근성 외부함수베이스클래스다른클래스 void function() class A private: private 멤버 protected: protected 멤버 public: public 멤버 ; class C ; class B : public A ; 파생클래스 protected 멤버는파생클래스에접근이허용된다.
protected 멤버상속 파생클래스의멤버함수는베이스클래스의공용멤버에직접접근이가능하지만베이스클래스의 private 멤버에대해서는접근할수없음 파생클래스의멤버함수가베이스클래스의 private 멤버를상속받아자유로이사용을위해서는다음두가지방식사용 프렌드함수를사용 베이스클래스정의시 protected 키워드를사용한보호부분을정의 보호부분에있는멤버는 private 멤버와같이외부에서는직접접근할수없지만파생클래스의멤버함수에서는직접접근이가능 보호부분멤버가파생클래스에서 public 으로상속받으면파생클래스의 protected 멤버가됨 private 으로상속받으면파생클래스에서 private 부분멤버가됨
protected 멤버상속예 상속관계에놓여있는경우접근허용 그이외는 private 멤버와동일 class AAA private: int a; protected: int b; ; class BBB: public AAA public: 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;
protected 상속과 private 상속 private 상속의결과 때문에이이상의상속은무의미할수있다. protected 상속의결과
06-4. 상속을위한조건
상속의조건 public 상속은 is-a 관계가성립되도록하자. 일반화 ParttimeStd 구체화
상속의조건 잘못된상속의예
상속의조건 HAS-A( 소유 ) 관계에의한상속! 경찰은몽둥이를소유한다 The Police have a cudgel.
#include <iostream> using std::endl; using std::cout; class Cudgel // 몽둥이 public: void Swing() cout<<"swing a cudgel!"<<endl; ; class Police : public Cudgel // 몽둥이를소유하는경찰 public: void UseWeapon() Swing(); ; int main() Police pol; pol.useweapon(); return 0; Police is a Cudgel (X) Police has a Cudgel (O)
상속의조건 HAS-A에의한상속그리고대안! 포함관계를통해서소유관계를표현 객체멤버에의한포함관계의형성 객체포인터멤버에의한포함관계의형성
상속의조건 /* 객체멤버예제 */ class Cudgel // 몽둥이 public: void Swing() cout<<"swing a cudgel!"<<endl; ; class Police // 몽둥이를소유하는경찰 Cudgel cud; public: void UseWeapon() cud.swing(); ; int main() Police pol; pol.useweapon(); return 0;
상속의조건 /* 객체포인터멤버예제 */ class Cudgel // 몽둥이 public: void Swing() cout<<"swing a cudgel!"<<endl; ; class Police // 몽둥이를소유하는경찰 Cudgel* cud; public: Police() cud=new Cudgel; ~Police() delete cud; void UseWeapon() cud->swing(); ; int main() Police pol; pol.useweapon(); return 0;
참고문헌 뇌를자극하는 C++ 프로그래밍, 이현창, 한빛미디어 1 열혈 C++ 프로그래밍 ( 개정판 ), 윤성우, 오렌지미디어 C++ ESPRESSO, 천인국저, 인피니티북스 명품 C++ Programming, 황기태, 생능출판사
추가자료
protected 로선언된멤버가허용하는접근의범위 private 을기준으로보면, protected 는 private 과달리상속관계에서의접근 을허용한다!
상속 접근예제 #include <iostream> #include <string> using namespace std; class Employee int rrn; // Regident Resgistration Number: 주민등록번호 protected: int salary; // 월급 public: ; string name; // 이름 void setsalary(int salary); int getsalary(); void Employee::setSalary(int salary) this->salary = salary; int Employee::getSalary() return salary;
class Manager : public Employee int bonus; public: Manager(int b=0) : bonus(b) void modify(int s, int b); void display(); ; void Manager::modify(int s, int b) salary = s; // 베이스클래스의보호멤버사용가능! bonus = b; void Manager::display() cout << " 봉급 : " << salary << " 보너스 : " << bonus << endl; // cout << " 주민등록번호 : " << rrn << endl; //
int main() Manager m; m.setsalary(2000); m.display(); m.modify(1000, 500); m.display(); 봉급 : 1000 보너스 : 500 계속하려면아무키나누르십시오...
private 상속예 다음에서컴파일오류가발생하는부분을찾아라. #include <iostream> using namespace std; class Base int a; protected: void seta(int a) this->a = a; public: void showa() cout << a; ; class Derived : private Base int b; protected: void setb(int b) this->b = b; public: void showb() cout << b; ; int main() Derived x; x.a = 5; x.seta(10); x.showa(); x.b = 10; x.setb(10); x.showb(); 컴파일오류 1, 2, 3, 4, 5 // 1 // 2 // 3 // 4 // 5 // 6
protected 상속예 다음에서컴파일오류가발생하는부분을찾아라. #include <iostream> using namespace std; class Base int a; protected: void seta(int a) this->a = a; public: void showa() cout << a; ; class Derived : protected Base int b; protected: void setb(int b) this->b = b; public: void showb() cout << b; ; int main() Derived x; x.a = 5; x.seta(10); x.showa(); x.b = 10; x.setb(10); x.showb(); 컴파일오류 1, 2, 3, 4, 5 // 1 // 2 // 3 // 4 // 5 // 6
상속의기본조건인 IS-A 관계의성립 무선전화기는전화기의기본기능에새로운특성이추가된것이다. 노트북컴퓨터는컴퓨터의기본기능에새로운특성이추가된것이다. 이렇듯 is-a 관계는논리적으로상속을기반으로표현하기에매우적절 하다.
IS-A 기반의예제 예제는도서본문을참조합시다!
ISA 상속예제 #include <iostream> #include <cstring> using namespace std; class Computer private: char owner[50]; public: Computer(char * name) strcpy(owner, name); void Calculate() cout<<" 요청내용을계산합니다."<<endl; ; class NotebookComp : public Computer private: int battary; public: 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; ;
class TabletNotebook : public NotebookComp private: char regstpenmodel[50]; public: 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;
HAS-A 관계를상속으로구성하면 경찰은총을소유한다. 경찰 has a 총! has a 관계도상속으로구현이가능하다. 하지만이러한경우 Police 와 Gun 은강한연관성을띠게된다. 따라서총을소유하지않은경찰이나, 다른무기를소유하는경찰을표현하기가쉽지않아진다.
HAS 상속예제 #include <iostream> #include <cstring> using namespace std; class Gun private: int bullet; // 장전된총알의수 public: Gun(int bnum) : bullet(bnum) void Shut() cout<<"bbang!"<<endl; bullet--; ; class Police : public Gun private: int handcuffs; // 소유한수갑의수 public: 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;
HAS-A 관계는포함으로표현한다 has a 의과계를포함의형태로표현하면, 두클래스간연관성은낮아지며, 변경및확장이용이해진다. 즉, 총을소유하지않은경찰의표현이쉬워지고, 추가로무기를소유하는형태로의확장도간단해진다.