728x90

상속(Inheritance)


상속에 대한 과거의 관점

: 기존에 정의해놓은 클래스의 재활용을 만들어진 문법적 요소



하지만 상속은 위와 같이 한 문장으로 정의할 수 있는 것이 아니며, 상속의 이점이 재활용에만 있는 것은 아니다.



데이터의 성격을 지닌 클래스 정의


급여관리 시스템에서 직원의 이름과 급여정보를 저장할 수 있는 클래스 정의


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PermanentWorker
{
private:
    char name[100];    
    int salary;
public:
    PermanentWorker(char* name, int money)
        : salary(money)
    {
        strcpy(this->name, name);
    }
    int GetPay() const
    {
        return salary;
    }
    void ShowSalaryInfo() const
    {
        cout<<"name: "<<name<<endl;
        cout<<"salary: "<<GetPay()<<endl<<endl;
    }
};
cs


control 클래스(handler 클래스) 정의

: 기능의 처리를 실제로 담당하는 클래스

  • 새로운 직원 정보의 등록                 AddEmployee
  • 모든 직원의 이번 달 급여정보 출력    ShowAllSalaryInfo
  • 이번 달 급여의 총액 출력                ShowTotalSalary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class EmployeeHandler
{
private:
    PermanentWorker* empList[50];
    int empNum;
public:
    EmployeeHandler() : empNum(0)
    { }
    void AddEmployee(PermanentWorker* emp)
    {
        empList[empNum++= emp;
    }
    void ShowAllSalaryInfo() const
    {
        for (int i = 0; i<empNum; i++)
            empList[i]->ShowSalaryInfo();
    }
    void ShowTotalSalary() const
    {
        int sum = 0;
        for (int i = 0; i<empNum; i++)
            sum += empList[i]->GetPay();
        cout << "salary sum: " << sum << endl;
    }
    ~EmployeeHandler()
    {
        for (int i = 0; i<empNum; i++)
            delete empList[i];
    }
};
cs

이러한 컨트롤 클래스는 기능 제공의 핵심이기 때문에 OOP에서 반드시 존재하는 클래스이다.



위의 두 클래스를 기반으로 하는 main함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(void)
{
    // 직원관리를 목적으로 설계된 컨트롤 클래스의 객체생성
    EmployeeHandler handler;
 
    // 직원 등록
    handler.AddEmployee(new PermanentWorker("KIM"1000));
    handler.AddEmployee(new PermanentWorker("LEE"1500));
    handler.AddEmployee(new PermanentWorker("JUN"2000));
 
    // 이번 달에 지불해야 할 급여의 정보
    handler.ShowAllSalaryInfo();
 
    // 이번 달에 지불해야 할 급여의 총합
    handler.ShowTotalSalary();
    return 0;
}
cs


Output :

name: KIM

salary: 1000


name: LEE

salary: 1500


name: JUN

salary: 2000


salary sum: 4500



위의 프로그램은 큰 문제가 없어보이지만, 객체지향뿐만 아니라 소프트웨어 설계에 있어 중요시하는 다음의 요소를 만족하지 않는다.

  • 요구 사항의 변경에 대응하는 프로그램의 '유연성'
  • 기능의 추가에 따른 프로그램의 '확장성'


 

만약 직원의 고용형태가 다양해지고, 급여 계산 방식도 달라진다면 클래스의 추가 및 컨트롤 클래스안에 여러 소스의 추가수정이 불가피해진다.

따라서 이러한 문제는 '상속'을 통해 해결이 가능하다.



상속의 문법적 이해


 현실에서 상속(물려받음)하면 재산만을 떠올리기 쉽지만, 물려받는 것은 재산이 아닌 여러 특성일 수도 있다.


"지수는 아버지로부터 좋은 목소리와 큰 키, 재산을 물려받았다"


class Jisu : public Father    // Father클래스의 상속을 의미 (Jisu 클래스가 Father클래스로부터 물려받음을 의미)



상속의 방법과 그 결과


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person
{
private:
    int age;        // 나이
    char name[50];     // 이름
public:
    Person(int myage, char * myname) : age(myage)
    {
        strcpy(name, myname);
    }
    void WhatYourName() const
    {
        cout << "My name is " << name << endl;
    }
    void HowOldAreYou() const
    {
        cout << "I'm " << age << " years old" << endl;
    }
};
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class UnivStudent : public Person    // Person 클래스를 상속 
{
private:
    char major[50];     // 전공과목
public:
    UnivStudent(char * myname, int myage, char * mymajor)
        : Person(myage, myname)
    {
        strcpy(major, mymajor);
    }
    void WhoAreYou() const 
    {
        WhatYourName();
        HowOldAreYou();
        cout<<"My major is "<<major<<endl<<endl;
    }
};
cs



// 다음 선언은 public 상속을 의미

class UnivStudent : public Person // Person 클래스의 상속을 의미

{

. . . .

}


이 때 상속받은 클래스의 객체 UnivStudent 객체의 구성은 다음과 같다.


상속을 하게 되면, 상속의 대상이 되는 클래스의 멤버가 객체내에 포함,

따라서 UnivStudent 클래스내에서 Person 클래스의 멤버 함수까지도 호출이 가능해진다.


상속과 관련된 용어

: Person과 같이 상속의 대상이 되는 클래스와 UnivStudent같이 상속을 하는 클래스를 다음과 같이 여러가지로 표현한다.


Person            ->    UnivStudent

상위 클래스        ->    하위 클래스

기초(base)클래스   ->    유도(derived) 클래스

슈퍼(super)클래스  ->    서브(sub) 클래스

부모 클래스        ->    자식 클래스  



유도 클래스(derived)의 생성자 정의


유도 클래스는 기초(base) 클래스의 초기할 데이터를 인자로 전달받을 책임과,

그 데이터를 기초 클래스의 생성자 호출을 통해 전달할 책임을 진다.


1
2
3
4
5
UnivStudent(char * myname, int myage, char * mymajor)
        : Person(myage, myname)
    {
        strcpy(major, mymajor);
    }
cs

유도 클래스의 생성자는 기초 클래스의 생성자를 호출하는 형태로 멤버 초기화가 이뤄진다.


유도클래스의 생성자에서 이니셜라이저가 의미하는 바는 생성자의 호출이다.

Person클래스의 생성자를 호출하면서 인자로 myage와 myname에 저장된 값을 전달하는 것이다.



상속에 관련된 예제


#include <iostream>
#include <cstring>
using namespace std;

class Person
{
private:
	char name[50];	// 이름
	int age;		// 나이	
public:
	Person(char * myname, int myage)
	{
		strcpy(name, myname);
		age = myage;		
	}
	void WhatYourName() const
	{
		cout << "My name is " << name << endl;
	}
	void HowOldAreYou() const
	{
		cout << "I'm " << age << " years old" << endl;
	}
};

class UnivStudent : public Person
{
private:
	char major[50];     // 전공과목
public:
	UnivStudent(char * myname, int myage, char * mymajor)
		: Person(myname, myage)
	{
		strcpy(major, mymajor);
	}
	void WhoAreYou() const
	{
		WhatYourName();
		HowOldAreYou();
		cout << "My major is " << major << endl << endl;
	}
};

int main(void)
{
	UnivStudent ustd1("Lee", 22, "Computer eng.");
	ustd1.WhoAreYou();

	UnivStudent ustd2("Yoon", 21, "Electronic eng.");
	ustd2.WhoAreYou();
	return 0;
};


Output :

My name is Lee

I'm 22 years old

My major is Computer eng.


My name is Yoon

I'm 21 years old

My major is Electronic eng.



위의 예제에서 보듯 Person의 멤버 name과 age는 private 선언이 되어 있지만
Person 클래스에 정의된 public 함수를 통해 간접적으로 접근이 가능하다.

유도 클래스의 생성자에서 기초 클래스의 생성자를 호출한다.
기초 클래스의 생성자에서는 클래스내 private의 멤버에 접근이 가능하다.

이렇게 생성자의 호출을 통해 기초 클래스의 멤버를 초기화할 수 있는 것이다.



+ Recent posts