상속

shinyeongwoon·2022년 11월 23일
0

C++

목록 보기
10/10

상속 inheritance

클래스 사이에서 상속관계 정의

  • 객체 사이에는 상속 관계 없음

기본 클래스의 속성과 기능을 파생 클래스에 물려주는것

  • 기본클래스 (base class) = 상속해주는 클래스, 부모 클래스
  • 파생클래스 (derived class) = 상속받는 클래스, 자식 클래스
    기본 클래스의 속성과 기능을 물려받고 자신만의 속성과 기능을 추가하여 확장

기본 클래스에서 파생 클래스로 갈수록 클래스의 개념이 구체화
다중 상속을 통한 클래스의 재 활용성을 높임

상속의 목적 및 장점

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

상속 선언

class Student:public Person{
	//person을 상속받는 Student 선언
    ...
};

class StudentWorker : public Student{
	//Student를 상속받는 StudentWorker 선언
    ...
}
  • Student 클래스는 Person 클래스의 멤버를 물려 받음
  • StudentWorker 클래스는 Student의 멤버를 물려받는다.
    student가 물려받은 Person의 멤버도 함께 물려받는다

Point class 를 상속 받는 ColorPoint class

#include <iostream>
#include <string>
#include <cstring>

using namespace std;

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{
  string color;
public:
  void setColor(string color){ this-> color = color;}
  void showColorPoint(); 
};

void ColorPoint::showColorPoint(){
  cout << color << ":";
  showPoint();
}

int main(){
  Point p;
  ColorPoint cp;
  cp.set(3,4);
  cp.setColor("Red");
  cp.showColorPoint();
}

업 캐스팅 (up-casting)

파생클래스 객체 포인터(우측)를 기본클래스 객체 포인터(좌측)로 치환

int main(){
	ColorPoint cp;
    ColorPoint *pDer = &cp;
    Point* pBase = pDep; // 업케스팅 (파생 클래스의 멤버 접근 불가)
    //pBase 포인터로 기본 클래스의 public 멤버만 접근 가능
    //pDer 포인터로 객체 cp의 모든 public 멤버 접근 가능
    
    pDer -> set(3,4);
    pBase -> showPoint();
    pDer -> setColor("Red");
    pDer -> showColorPoint();
    pBase -> showColorPoint(); // 컴파일 오류
}

다운 캐스팅 (down-casting)

기본 클래스의 객체 포인터를 파생 클래스의 객체 포인터로 치환

int main(){
	ColorPoint cp;
    ColorPoint *pDer;
    Point* pBase = &cp; //업캐스팅
    //pBase 포인터로 기본 클래스의 public 멤버만 접근 가능
    pBase -> set(3,4);
    pBase -> showPoint();
    
    pDer = (ColorPoint *)pBase; //다운 캐스팅, 강제 형변환 반드시 필요
    //pDer 포인터로 cp의 모든 public 멤버 접근 가능
    pDer -> setColor("Red");
    pDer -> showColorPoint();
}

접근 지정자

private 멤버

  • 선언된 클래스 내에서만 접근 가능
  • 파생 클래스에서도 기본 클래스의 private 멤버 직접 접근 불가
    public 멤버
  • 선언된 클래스나 외부 어떤 클래스, 모든 외부 함수에 접근 허용
  • 파생 클래스에서 기본 클래스의 public 멤버 접근 가능
    protected 멤버
  • 선언된 클래스에서 접근 가능
  • 파생 클래스에서만 접근 허용 : 파생 클래스가 아닌 다른 클래스나 외부 함수에서는 protected 멤버를 접근 할 수 없다.

#include <iostream>
#include <string>

using namespace std;

class Point {
protected:
  int x, y; //한 점 (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 << "," << y << ")" << endl; 
}

class ColorPoint : public Point {
  string color;
public:
  void setColor(string color);
  void showColorPoint();
  bool equals(ColorPoint p); };
  void ColorPoint::setColor(string color) {
  this->color = color;  
}

void ColorPoint::showColorPoint() {
  cout << color << ":";
  showPoint(); // Point 클래스의 showPoint() 호출
}

bool ColorPoint::equals(ColorPoint p) {
  if(x == p.x && y == p.y && color == p.color) // 접근 가능
    return true;
  else 
    return false; 
}

int main() {
  Point p; // 기본 클래스의 객체 생성
  p.set(2,3);
  p.x = 5; //오류 접근 못함 : protected 영역
  p.y = 5; //오류 접근 못함 : protected 영역
  p.showPoint(); 
  ColorPoint cp; // 파생 클래스의 객체 생성
  cp.x = 10; // 오류 접근 못함 : protected 영역
  cp.y = 10; // 오류 접근 못함 : protected 영역
  cp.set(3,4);
  cp.setColor("Red"); 
  cp.showColorPoint();
  ColorPoint cp2;
  cp2.set(3,4);
  cp2.setColor("Red");
  cout << ((cp.equals(cp2))?"true":"false"); 
}

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

파생 클래스가 생성 될 때:

  • 기본 클래스의 생성자와 파생클래스 생성자 모두 실행 됨
  • 기본 클래스 생성자가 먼저 실행 되고 파생 클래스 생성자가 실행 됨

소멸자 실행 순서 : 역순
파생 클래스의 소멸자가 먼저 소멸 되고 기본 클래스 소멸자가 나중에 실행

컴파일러에 의해 묵시적으로 기본 클래스의 생성자를 요청

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

파생 클래스의 생성자에서 명시적으로 기본 클래스의 생성자 요청 (위임 생성자)

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

상속 지정

상속 선언 시 public, private, protected의 3가지 중 하나 지정
기본 클래스의 멤버의 접근 속성을 어떻게 계승할지 지정

  • public : 기본 클래스의 protected, public, 멤버 속성을 그대로 계승
  • private : 기본 클래스의 protected, public 멤버를 private으로 계승
  • protected : 기본 클래스의 protected,public 멤버를 protected로 계승

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 { //private 계승으로 사용 불가

	int b;

protected:

	void setB(int b) { this->b = b; }

public:

	void showB() { cout << b; }

};

int main() {

	Derived x;

	x.a = 5; // ① Base의 private 멤버로 접근 불가

	x.setA(10); // ② private 계승으로 접근 불가

	x.showA(); // ③ private 계승으로 접근 불가

	x.b = 10; // ④ 외부 함수 접근으로 접근 불가

	x.setB(10); // ⑤ 외부 함수 접근으로 접근 불가

	x.showB(); // ⑥ public으로 접근 가능

} 

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; // ① Base 의 private 멤버

	x.setA(10); // ② protected 계승 이지만 외부 함수 접근으로 사용 불가

	x.showA(); // ③ protected 계승 이지만 외부 함수 접근으로 사용 불가

	x.b = 10; // ④ 외부 함수 접근으로 사용 불가

	x.setB(10); // ⑤ 외부 함수 접근으로 사용 불가

	x.showB(); // ⑥ public 으로 사용 가능

}

상속이 중첩될 때 접근 지정 사례

#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() {

	setA(5); // ① private 계승으로 자신은 사용가능

	showA(); // ② private 계승으로 자신은 사용가능

	cout << b;

}

};

class GrandDerived : private Derived {

	int c;

protected:

	void setAB(int x) {

	setA(x); // ③ 컴파일 오류

	showA(); // ④ 컴파일 오류

	setB(x); // ⑤

	}

};

C++의 다중 상속

class MP3 {

public:

	void play();

	void stop();

};

class MobilePhone {

public:

	bool sendCall();

	bool receiveCall();

	bool sendSMS();

	bool receiveSMS();

};

class MusicPhone : public MP3, public MobilePhone { // 다중 상속 선언

public:

	void dial();

};

void MusicPhone::dial() {

	play(); // mp3 음악을 연주시키고

	sendCall(); // 전화를 건다.

}

int main() {

	MusicPhone hanPhone;

	hanPhone.play(); // MP3의 멤버 play() 호출

	hanPhone.sendSMS(); // MobilePhone의 멤버 sendSMS() 호출

}

다중 상속의 문제점

기본 클래스 멤버의 중복 상속

  • BaseIO의 멤버가 이중적으로 객체에 삽입되는 문제점
  • 동일한 x를 접근하는 프로그램이 서로 다른 x에 접근하는 결과를 낳게 되어 잘못된 실행 오류가 발생

가상 상속

다중 상속으로 인한 기본 클래스 멤버의 중복 상속 해결

  • 파생 클래스의 선언문에서 기본 클래스 앞에 virtual로 선언
  • 파생 클래스의 객체가 생성될 때 기본 클래스의 멤버는 오직 한 번만 생성
    기본 클래스의 멤버가 중복 되는것을 방지
class in : virtual public BaseIO{ // in 클래스는 BaseIO 클래스를 가상 상속함


}

class out : virtual public BaseIO{// out 클래스는 BaseIO 클래스를 가상 상속함

}

0개의 댓글