강께르의 개발일지

[C++] 클래스의 상속(생성자, 소멸자, 접근 제어 지시자) 본문

프로그래밍/C++

[C++] 클래스의 상속(생성자, 소멸자, 접근 제어 지시자)

강께르 2021. 6. 15. 11:58

1. 상속을 쓰는 이유

- 이는 열혈 C++ 프로그래밍 책의 예시를 요약해서 적어둔 것입니다.

- A 회사에서 운영하는 급여 관리 시스템을 만들려고 한다. 정규직을 뜻하는 PermanentWorker는 이름과 급여 액수가 멤버 변수로 선언되어 있고 생성자, 멤버 변수에 대한 Get함수 그리고 멤버 변수의 정보를 출력하는 함수로 클래스가 정의되어 있다.

- 이 클래스를 객체로 만들어 50개를 저장할 객체 배열을 멤버변수로 만들 것이다. 이 멤버 변수를 지니는 클래스는 EmployeeHandler 클래스이다. EmployeeHandler는 정규직을 뜻하는 객체 배열을 통해 직원을 관리하고 함수로 실 급여액을 계산하거나 정규직 객체를 배열에 동적 할당으로 추가시키는 일 등을 할 것이다.

 

- 여기까지가 A 회사 급여관리 시스템의 개요이고 이후 요구 사항에 대한 이야기를 하겠다.

- 상속을 쓰는 이유는

"요구 사항의 변경에 대응하는 프로그램의 유연성"

"기능 추가에 따른 프로그램의 확장성"

                                에 장점이 있어서 사용을 한다.

- 이를 상기하며 위의 예시가 유연성과 확장성을 확보했는지 여부를 확인할 것이다.

- A 회사에서 사업이 워낙 잘 되었나본지 직원의 종류가 세분화되고 그 직원에 따른 급여 계산도 달라졌다고 요구사항을 보내줬다. 그 요구사항은 영업직과 임시직을 추가해달라는 것이다.

- 영업직은 인센티브가 추가되고, 임시직은 시간당 급여로 급여액을 계산한다는 특징이 있다.

- 위의 예시로 이런 기능을 추가한다면 PermanentWorker 클래스를 만들었듯이 두 클래스를 새로 만들어서 EmployeeHandler 클래스에 멤버 변수로 객체 배열을 새로 만든 클래스마다 선언해주면 되지 않냐고 생각할 수 있다.

- 그리고 영업직과 임시직은 정규직과 급여액을 계산하는 공식이 다르기 때문에 EmployeeHandler 클래스에 다른 이름의 함수를 또 만들면 끝이지 않냐고 생각할 수도 있다.

- 그런데 이러면 EmployeeHandler 클래스의 수정은 최소화되지 않고 아예 연관없이 새로 다 만들어야 하거나 거의 전부를 들어내는 듯한 수정을 진행해야 한다. 확정성과 유연성에 있어서 좋다고 이야기할 수 있으려면 EmployeeHandler 클래스에 수정을 최소화해야 하는데 이를 위해 필요한 개념이 상속이다.

 

2. 상속이란?

- 상속은 상속해주는(상위, 기초, 부모) 클래스로부터 상속을 받는(하위, 유도, 자식) 클래스에게 가지고 있는 특성을 물려주는 개념이다. 이 때 특성이란, 부모 클래스의 멤버 변수, 멤버 함수를 자식 클래스에게 똑같이 쓸 수 있도록 한다는 것이다.

- 상속에 대한 클래스 선언은 다음과 같이 할 수 있다.

class Parent
{
public:
	void Print();
private:
	int a;
	int b;
};

class Child : public Parent
{ };

- 위의 코드를 설명하자면, Parent라는 클래스를 선언하고 멤버함수 Print와 정수형 변수 a, b를 선언하였다.

- 그 아래줄에 이어서 Child라는 클래스를 선언하는데 : 를 사용함으로 그 뒤에 따라오는 클래스 이름을 상속받겠다는 의미로 사용한다.

- 그래서 Child 클래스는 Body문에 아무것도 없지만 Parent 클래스의 멤버 변수와 멤버 함수를 모두 상속받아서 갖고 있다고 생각할 수 있다.

 

3. 상속에서의 생성자

- 상속받은 클래스의 생성자는 어떻게 정의할까? 위의 두 클래스를 통해 Child 클래스의 생성자를 정의해보자.

Parent::Parent(int num1, int num2) : a(num1), b(num2) { }

Child::Child(int val1, int val2) : Parent(val1, val2) { }

- Child 클래스로 객체를 생성하면 그가 지닐 멤버변수에 대해 초기화를 해야 한다. Child 생성자에서 멤버 이니셜라이저로 부모 클래스인 Parent 클래스의 생성자를 호출하여 초기화할 변수에게 매개변수를 전달해준다.

- 그리고 부모 클래스의 생성자로 상속받은 멤버변수에 대한 초기화를 한다.

 

- 정리하자면 Child 클래스의 생성자는 Parent 클래스의 생성자를 호출해서 Parent 클래스의 멤버를 초기화해야 하는 것이다.

- 자식 클래스에서 부모 클래스의 생성자는 무조건 호출되고, 만약 자식 클래스의 생성자에서 부모 클래스의 생성자 호출을 명시하지 않았다면, 부모 클래스의 매개변수 없는 void 생성자가 호출될 것이다.

- 클래스의 멤버는 해당 클래스의 생성자를 통해 초기화해야한다는 기본 원칙을 상속 개념에서도 동일하게 적용하는 것이다.

 

4. 상속에서의 소멸자

- 상속 개념에서의 소멸자는 생성자가 호출한 순서의 반대로 호출된다.

- 스택에 생성된 객체는 선입선출 방식이 아닌 선입후출 방식으로 생성되고 소멸한다.

 

5. 상속에서의 접근 제어 지시자

- 상속 관계의 클래스를 보면 설명을 하지 않은 부분이 있다.

class Child : public Parent { };

- 클래스, 클래스 이름, 부모 클래스 이름까지는 알겠으나 public의 의미를 파악할 필요가 있다.

- public은 접근 제어 지시자의 한 종류로 접근 제어 지시자는 public을 포함하여 protected, private까지 세 개의 종류로 이루어져 있다.

- 상속할 때, 부모 클래스의 앞 자리에 쓰여서 자식 클래스에게 멤버 변수를 어느 정도 접근이 가능한지 명시하기 위한 용도로 쓰인다. 이는 '정보 은닉'이라는 특징을 지키는 방법이다.

 

 5-1. protected의 상속

- protected는 protected보다 접근 범위가 넓은 멤버 변수를 protected로 변경해서 상속하겠다는 것이다.

- 이 말은 부모 클래스의 멤버 변수 중 public으로 선언한 변수가 있다면 자식 클래스에서는 protected로 변경시키겠다는 것이다.

 

 5-2. private의 상속

- private의 상속은 private보다 접근 범위가 넓은 멤버 변수를 private로 변경해서 상속하겠다는 것이다.

- 이 말은 부모 클래스의 멤버 변수 중 public, protected으로 선언한 변수가 있다면 자식 클래스에서는 private로 변경시키겠다는 것이다.

- private로 선언된 멤버 변수는 자기 자신 클래스 이외는 접근할 수 없는 접근 제어 지시자이므로 만약 자식 클래스에서 private로 변경된 멤버 변수라면 다시 그 자식 클래스로 상속할 때에는 private가 아닌 접근할 수 없는 변수로 변경될 것이다.

 

 5-3. public의 상속

- public의 상속은 private을 제외한 나머지를 그냥 그대로 상속한다는 것이다. private는 자식 클래스에 와서 접근이 불가능한 거 이외에는 모두 그대로 상속하는 것이다.

 

- public 이외의 상속은 다중 상속과 같은 특별한 경우가 아니면 잘 사용하지 않는다고 한다.

 

- 상속에 대한 내용은 이어서 계속됩니다.