Search
Duplicate

8장. 상속 (Inheritance)

생성일
2022/04/27 01:34
태그
C++

8장. 상속 (Inheritance)

유전적 상속

객체 지향 상속

C++ 에서의 상속 (Inheritance)

C++에서 상속?
클래스 사이에서 상속관계를 정의한다
객체 사이에는 상속 관계 없다
기본 클래스의 속성과 기능을 파생 클래스에 물려주는 것
기본 클래스 - 상속해주는 클래스, 부모 클래스
파생 클래스(derived class) - 상속받는 클래스, 자식 클래스
기본 클래스의 속성과 기능을 물려받고 자신 만의 속성과 기능을 추가하여 작성한다.
기본 클래스에서 파생 클래스로 갈수록 클래스의 개념이 구체화
다중 상속을 통한 클래스의 재활용성 높임

상속의 표현

간단한 상속 예)
class Phone { void call(); void receive(); }; // Phone을 상속받는다 class MobilePhone : public Phone { void connectWireless(); void recharge(); }; // MobilePhone을 상속받는다 class MusicPhone : public MobilePhone { void downloadMusic(); void play(); };
C++
복사

상속의 목적 및 장점

1.
간결한 클래스 작성
기본 클래스의 기능을 물려받아, 파생 클래스를 간결하게 작성가능
2.
클래스 간의 계층적 분류 및 간리의 용이함
상속은 클래스들의 구조적 관계 파악 용이
3.
클래스 재사용과 확장을 통한 소프트웨어 생산성 향상
빠른 소프트웨어 생산 필요
기존에 작성한 클래스의 재사용 - 상속
상속받아 새로운 기능을 확장
앞으로 있을 상속에 대비한 클래스의 객체 지향적 설계 필요

상속 선언

class Student : public Person { // 파생클래스명, 상속접근지정, 기본클래스명 // Person을 상속받는 Student 선언 ....... }; class StudentWorker : public Student { // Student를 상속받는 StudentWorker 선언 ....... };
C++
복사
Student 클래스는 Person 클래스의 멤버를 물려받는다
StudentWorker 클래스는 Student의 멤버를 물려받는다
Student가 물려받은 Person의 멤버도 함께 물려받는다
예제 8-1. Point 클래스를 상속받는 ColorPoint 클래스 만들기
#include <iostream> #include <string> using namespace std; // 2차원 평면에서 한 점을 표현하는 클래스 Point 선언 class Point { int x, y; public: void set(int x, int y) { this->x = x; this->y = y; } void showPoint() { cout << "(" << x << ", " << y << ")" << endl; } }; class ColorPoint : public Point { // 2차원 평면에서 컬러점을 표현하는 클래스 ColorPoint. // Point를 상속받음 string color; // 점의 색 표현 public: void setColor(string color) { this->color = color; } void showColorPoint(); }; void ColorPoint::showColorPoint() { cout << color << " : "; showPoint(); // Point의 showPoint() 호출 } int main() { Point p; //기본 클래스의 객체 생성 ColorPoint cp; // 파생 클래스의 객체 생성 cp.set(3, 4); // 기본 클래스의 멤버 호출 cp.setColor("Red"); // 파생 클래스의 멤버 호출 cp.showColorPoint(); // 파생 클래스의 멤버 호출 }
C++
복사

파생 클래스의 객체 구성

파생 클래스에서 기본 클래스 멤버 접근

외부에서 파생 클래스 객체에 대한 접근

상속과 객체 포인터 - 업 개스팅

업 개스팅 (up-casting)

파생 클래스 포인터가 기본 클래스 포인터에 치환되는 것
ex) 사람을 동물로 봄
int main() { ColorPoint cp; ColorPoint *pDer = &cp; Point* pBase = pDer; // 업캐스팅 pDer->set(3, 4); pBase->showPoint(); pDer->setColor("Red"); pDer->showColorPoint(); pBase->showColorPoint(); // 컴파일 오류 }
C++
복사
업캐스팅 이해하기! (업캐스팅 개념)
→ 생물을 가리키는 손가락으로 어류, 포유류, 사람, 식물 등 생물의 속성을 상속받은 객체들을 가리키는 것은 자연스럽다
→ 생물을 가리키는 손가락으로 컵 같은 관려없는 것을 가리키면 오류

다운 캐스팅 (down-casting)

기본 클래스의 포인터가 파생 클래스의 포인터에 치환되는 것
int main() { ColorPoint cp; ColorPoint *pDer; Point* pBase = &cp; // 업캐스팅 pBase->set(3, 4); pBase->showPoint(); pDer = (ColorPoint *)pBase; // 다운캐스팅 // 강제 타입 변환 반드시 필요하다 pDer->setColor("Red"); // 정상 컴파일 pDer->showColorPoint(); // 정상 컴파일 }
C++
복사

protected 접근 지정

접근 지정자

private 멤버
선언된 클래스 내에서만 접근 가능
파생 클래스에서도 기본 클래스의 private 멤버 직접 접근 불가
public 멤버
선언된 클래스나 외부 어떤 클래스, 모든 외부 함수에 접근 허용
파생 클래스에서 기본 클래스의 public 멤버 접근 가능
protected 멤버
선언된 클래스에서 접근 가능
파생 클래스에서만 접근 허용
파생 클래스가 아닌 다른 클래스나 외부 함수에서는 protected 멤버를 접근할 수 없다
멤버의 접근 지정에 따른 접근성시험문제 언급
예제 8-2, protected 멤버에 대한 접근 ( Error )
#include <iostream> #include <string> using namespace std; // 2차원 평면에서 한 점을 표현하는 클래스 Point 선언 class Point { int x, y; public: void set(int x, int y); void showPoint(); }; void Point::set(int x, int y) { this->x = x; this->y = y; } void Point::showPoint() { cout << "(" << x << ", " << ")" << endl; } class ColorPoint : public Point { // 2차원 평면에서 컬러점을 표현하는 클래스 ColorPoint. // Point를 상속받음 string color; // 점의 색 표현 public: void setColor(string color) { this->color = color; } void showColorPoint(); bool equals(ColorPoint p); }; void ColorPoint::showColorPoint() { cout << color << " : "; showPoint(); // Point의 showPoint() 호출 } bool ColorPoint::equals(ColorPoint p) { if (x == p.x && y == p.y && color == p.color) // Error 포인트 return true; else return false; } int main() { Point p; // 기본 클래스의 객체 생성 p.set(2, 3); p.x = 5; // Error p.y = 5; // Error p.showPoint(); ColorPoint cp; // 파생 클래스의 객체 생성 cp.x = 10; // Error cp.y = 10; // Error cp.set(3, 4); cp.setColor("Red"); cp.showColorPoint(); ColorPoint cp2; cp2.set(3, 4); cp2.setColor("Red"); cout << ((cp.equals(cp2))?"true":"false"); }
C++
복사

상속 관계의 생성자와 소멸자 실행

Q1) 파생 클래스의 객체가 생성될 때, 파생 클래스의 생성자와 기본 클래스의 생성자가 모두 실행되는가? 아니면 파생 클래스의 생성자만 실행되는가?
A1) 파생 클래스의 생성자와 기본 클래스의 생성자 모두 실행된다.
Q2) 파생 클래스의 생성자와 기본 클래스의 생성자 중 어떤 생성자가 먼저 실행되는가?
A2) 기본 클래스의 생성자가 먼저 실행된 후, 파생 클래스의 생성자가 실행된다.
→ 반대로 소멸자는 파생 클래스의 소멸자부터 실행된 후, 기본 클래스의 소멸자가 실행된다.
생성자 호출 관계 및 실행 순서

소멸자의 실행 순서

파생 클래스의 객체가 소멸될 때
파생 클래스의 소멸자가 먼저 실행되고
기본 클래스의 소멸자가 나중에 실행된다

컴파일러에 의해 묵시적으로, 기본 클래스의 생성자를 선택하는 경우

→ 파생 클래스의 생성자에서 기본 클래스의 기본 생성자 호출
→ 따로 선택하지 않으면, 부모 클래스의 기본 생성자를 호출하는 것이 디폴트 동작.

기본 클래스에 기본 생성자가 없는 경우

→ Error : 사용할 수 있는 적절한 기본 생성자가 없습니다.

매개 변수를 가진 파생 클래스의 생성자는 묵시적으로 기본 클래스의 기본 생성자 선택

→ 파생 클래스의 매개 변수를 가진 생성자가 기본 클래스의 기본 생성자 호출

파생 클래스의 생성자에서 명시적으로 기본 클래스의 생성자 선택

컴파일러의 기본 생성자 호출 코드 삽입

class B { B() : A() { // 컴파일러가 묵시적으로 삽입한 코드 cout << "생성자 B" << endl; } B(int x) : A() { // 컴파일러가 묵시적으로 삽입한 코드 -> 자동으로 부모의 생성자를 호출하도록 세팅 cout << "매개변수생성자 B" << x << endl; } };
C++
복사
예제 8-3. TV, WideTV, SmartTV 생성자 매개 변수 전달
#include <iostream> #include <string> using namespace std; class TV { int size; public: TV() { size = 20; } TV(int size) { this->size = size; } int getSize() { return size; } }; class WideTV : public TV { bool videoIn; public: WideTV(int size, bool videoIn) : TV(size) { this->videoIn = videoIn; } bool getVideoIn() { return videoIn; } }; class SmartTV : public WideTV { string ipAddr; public: SmartTV(string ipAddr, int size) : WideTV(size, true) { this->ipAddr = ipAddr; } string getIpAddr() { return ipAddr; } }; int main() { SmartTV htv("192.0.0.1", 32); cout << "size= " << htv.getSize() << endl; cout << "videoIn= " << boolalpha << htv.getVideoIn() << endl; cout << "IP=" << htv.getIpAddr() << endl; }
C++
복사

상속 지정

기본 클래스의 멤버의 접근 속성을 어떻게 계승할지 지정한다
public
기본 클래스의 protected, public 멤버 속성을 그래도 계승
private
기본 클래스의 protected, public 멤버를 private으로 계승
protected
기본 클래스의 protected, public 멤버를 protected로 계승

상속 시 접근 지정에 따른 멤버의 접근 지정 속성 변화 (중요)

예제 8-4, private 상속 사례
1, 2, 3 - 모두 private으로 보여서 에러
4 -
5 - protected니까 바깥쪽에서 접근 x
상속 사례 꼭 이해하고, 두세번 보고 넘어갈 것
예제 8-7) 다중상속받는 Calculator 클래스
#include <iostream> using namespace std; class Adder { protected: int add (int a, int b) { return a + b; } }; class Subtractor { protected: int minus (int a, int b) { return a - b; } }; // 다중 상속 class Calculator : public Adder, public Subtractor { public: int calc (char op, int a, int b); }; int Calculator::calc (char op, int a, int b) { int res = 0; switch(op) { case '+': res = add(a, b); break; case '-': res = minus(a, b); break; } return res; } int main() { Calculator handCalculator; cout << "2 + 4 = " << handCalculator.calc('+', 2, 4) << endl; cout << "100 - 8 = " << handCalculator.calc('-', 100, 8) << endl; }
C++
복사

가상 상속

다중 상속으로 인한 기본 클래스 멤버의 중복 상속 해결
가상 상속
파생 클래스의 선언문에서 기본 클래스 앞에 virtual으로 선언
파생 클래스의 객체가 생성될 때, 기본 클래스의 멤버는 오직 한번만 생성
⇒ 기본 클래스의 멤버가 중복하여 생성되는 것을 방지
class In : virtual public BaseIO { // In 클래스는 BaseIO 클래스를 가상 상속 }; class Out : virtual public BaseIO { // Out 클래스는 BaseIO 클래스를 가상 상속 };
C++
복사