본문 바로가기
C++

상속

by oncerun 2020. 11. 12.
반응형

객체지향 언어에서 상속은 계층 관계를 사용하여 클래스 간의 속성 및 함수를 공유할 수 있도록 지원하는 중요한 개념이다.

하나의 클래스에서 파생된 클래스들은 상위 클래스의 같은 개념을 가지고 확장된 클래스라 할 수 있으며 이렇게 분할된 클래스를 파생 클래스라고 한다. 파생 클래스의 상위 개념의 클래스들은 기초 클래스라고 한다.

 

c++에서 파생 클래스를 선언하는 방법은 다음과 같다.

class DClassName : visivilitySpec BClassName{

visibilitySpec_1:
	데이터 멤버 또는 멤버함수 리스트;

}

*DClassName : 파생 클래스 이름
*BClassName : 기초 클래스 이름
*visibilitySpec : 가시성 지시어 -> public,protected,private

파생 클래스는 파생 클래스의 이름 옆에 콜론(:)과 기초 클래스의 이름을 기록하여 선언한다.

기초 클래스가 갖는 특성 이외에 추가되는 고유 데이터 멤버나, 고유 함수들을 {} 안에 기록한다.

 

 

파생 클래스의 생성자 및 소멸자

 

기초 클래스가 생성자를 가지고 있다면, 파생 클래스의 생성자는 기초 클래스의 생성자를 호출하고 나머지 필요한 부분만 정의한다.

기초 클래스의 생성자가 인수를 필요로 하면, 그 인수들을 넘겨주어야 한다. 파생 클래스는 기초 클래스를 바탕으로 만들어 지므로 파생 클래스의 생성자가 수행될 때 기초 클래스의 생성자가 먼저 호출되어 실행되고, 그다음에 파생 클래스에서 구현한 명령문들이 실행된다.

 파생 클래스의 소멸자를 정의할 때에는 파생 클래스의 정리 작업만 하면 된다. 이때 기초 클래스의 소멸자는 자동으로 호출된다. 실행 순서는 생성자와 반대로 파생 클래스의 소멸자에 나열된 명령문이 먼저 실행된 뒤 기초 클래스의 소멸자가 실행된다.

 

 

액세스 제어

클래스는 다른 클래스들이나 외부 함수가 자신의 멤버 함수들을 액세스 하는 것을 허가하거나 금지함으로써 캡슐화할 수 있는 기능이 있다.

 

이러한 기능은 지시어 (public, private , protected)를 사용하여 사용할 수 있는데,

 

public은 데이터 멤버나 함수를 파생 클래스 혹은 외부 클래스들에게 공개하는 것을 의미한다.

private은 파생 클래스, 외부 클래스들에게 비공개함으로써 감춰야 하는 속성이나 기능을 private영역 안에다 작성한다.

protected는 파생 클래스에게만 공개될 수 있도록 허가하고자 하는 멤버들을 작성한다.

추가적으로 친구 클래스를 friend로 지정하면 private , protected에 지정된 멤버들에도 외부에서 접근할 수 있다.

 

클래스의 상속에서 가시성을 지정하는 부분이 존재했다.

이런 가시성 상속 지시어는 기초 클래스로부터 상속된 멤버가 패상 클래스의 멤버로서 가지게 되는 가시성의 상한을 나타내는 것이다.

 

public으로 기초 클래스를 상속받을 경우에는 다음과 같다.

파생 클래스의 public 멤버에는 기초 클래스의 public멤버들이 포함된다.

따라서 외부 함수에서 파생 클래스의 public 멤버와 기초 클래스의 public멤버까지 접근할 수 있다.

또한 파생 클래스에서는 기초 클래스의 public과 protected의 멤버까지 접근할 수 있다.

 

private으로 기초 클래스를 상속받을 경우에는 다음과 같다.

파생 클래스는 기초 클래스의 public 및 protected 멤버들은 파생 클래스에서만 공개되고 외부에 대해서는 private으로 가시성이 제한된다. 외부에서는 파생 클래스를 통해 기초 클래스의 멤버들에 전부 접근할 수 없다.

 

protected으로 가시성 상속 지시어를 사용한 경우에는 기초 클래스 멤버의 최대 가시성은 protected이다 따라서 기초 클래스의 public , protected 멤버들은 파생 클래스의 protected멤버로 취급된다. 따라서 외부에서는 전부 접근할 수 없다.

다만 파생 클래스에서는 기초 클래스의 public, protected멤버에 접근할 수 있다.

 

 

파생 클래스와 포인터

 기초 클래스의 포인터는 파생 클래스의 객체를 가리킬 수 있다. 하지만 그 파생 클래스의 포인터는 기초 클래스의 객체를 가리킬 수 없다. 왜냐면 파생 클래스의 포인터를 가지고 접근할 수 있는 멤버에는 기초 클래스에 선언되어 있지 않은 파생 클래스의 특수한 멤버들이 포함될 수가 있기 때문이다.

 

그런데 여기서 문제가 있다. 기초 클래스의 포인터 배열을 생성한 다음 기초 클래스 배열 포인터에 파생 클래스의 객체를 적재한다.

그리고 해당 배열에 있는 객체의 함수를 실행하는데 자식 객체가 해당 함수를 오버라이딩 했지만 기초 클래스의 멤버 함수가 출력되는 걸 볼 수 있는데, 실행할 때 배열에 연결된 객체가 기초 클래스에 속하지는 파생 클래스에 속하는지 알 수 없다. 하지만 이들을 가리키는 포인터는 기초 클래스에 대한 포인터이므로 전부 기초 클래스의 멤버가 사용되는 것이다. 이를 해결하기 위한 방법은 가상 함수를 이용하는 것이다.

 

가상 함수

 

정적 연결 

- 포인터 혹은 참조에 의하여 함수가 호출될 때, 일반적으로 포인터 혹은 참조의 유형에 따라 호출되는 멤버 함수가 결정된다. 이러한 방식을 정적 연결이라고 하며, 컴파일될 때 실행할 멤버함수가 결정된다.

 

동적 연결 

- 프로그램이 실행될 때 실제 객체에 따라 멤버 함수를 결정하는 방법이다. 동적 연결을 구현하는 방법이 바로 가상 함수이다.

가상 함수는 다음과 같이 기초 클래스의 함수 앞에 예약아 virtual을 붙여서 정의한다.

virtual ReturnType functionName(fParameterList);

ReturnType : 멤버함수의 반환 자료형
functionName : 멤버함수의 이름
fParameterList : 멤버함수의 형식 매개변수 목록

가상 함수가 설정된 클래스들은 가상함수 테이블과 가상함수 테이블을 참조하는 가상 함수 포인터를 가지고 있는데,

이런 구조로 기초 클래스의 포인터가 파생 클래스의 객체를 가리키게 되어도 객체에 해당하는 함수를 실행할 때에는 동적으로 찾아가 실행하게 된다.

반응형

'C++' 카테고리의 다른 글

템플릿  (0) 2020.11.12
추상 클래스  (0) 2020.11.12
연산자 다중정의  (0) 2020.11.10
함수  (0) 2020.11.08
구조체와 클래스  (0) 2020.11.08

댓글