도순씨의 코딩일지

C++ :: 상속(Inheritance) 본문

𝐏𝐑𝐎𝐆𝐑𝐀𝐌𝐌𝐈𝐍𝐆/𝐂++

C++ :: 상속(Inheritance)

도순씨 2020. 8. 25. 14:39

🌼 상속의 개념

상속은 기존에 정의해 놓은 클래스의 재활용을 목적으로 합니다. 먼저 예제를 하나 살펴봅시다.

 

⭐️ EmployeeManger1.cpp

 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <cstring>
using namespace std;
 
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;
    }
};
 
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];
    }
};
 
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

 

⭐️ EmployeeManger1.cpp 실행결과

 

1
2
3
4
5
6
7
8
9
10
name: KIM
salary: 1000
 
name: LEE
salary: 1500
 
name: JUN
salary: 2000
 
salary sum: 4500
cs

 

여기는 고용형태가 정규직(Permanent) 하나였습니다. 다른 고용형태가 추가되었다면 어떻게 고쳐야할까요?

 

💡 영업직(Sales): 조금 특화된 형태의 고용직. 인센티브 개념이 도입

💡 임시직(Temporary): 학생들을 대상으로 하는 임시고용의 형태. 아르바이트.

 

영업직은 판매실적에 따른 인센티브 제도까지 적용합니다. 급여는 다음과 같이 적용됩니다

💡 고용직 급여: 연봉제. 매달의 급여가 정해져있음.

💡 영업직 급여: 기본급여 + 인센티브

💡 임시직 급여: 시간당 급여 * 일한 시간

 

하지만 세 개의 클래스를 각각 만들어서 해당하는 변수와 함수를 작성하는 것은 매우 번거로운 일입니다. 이 때문에 상속의 개념이 시작되었습니다.

 

상속은 클래스가 가진 변수와 함수를 말 그대로 '상속'시켜주는 것입니다. 먼저 예제를 살펴보도록 합시다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person{
private:
    int age;        // 나이
    char name[50];  // 이름
public:
    Person(int myage, char * myname) : age(myage){
        strcpy(name, myname);
    }
    
    void WhatsYourName() 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
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{
        WhatsYourName();
        HowOldAreYou();
        cout<<"My major is " << major << endl << endl;
    }
};
cs

 

위 코드의 첫 번째 라인이 public 상속입니다. 9번째와 10번째 라인을 보면 두 함수가 정의되어 있지 않음에도 불구하고 정상적으로 실행됩니다.

 

 

 

🌼 상속받은 클래스의 생성자 정의

앞서 살펴본 UnivStudent 클래스의 생성자는 다음과 같습니다.

 

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

 

Person(myage, myname)을 통해서 Person 클래스의 멤버를 초기화한다는 것을 알 수 있습니다. 또, 간단하게 Person 클래스의 생성자를 호출해서 초기화를 해주면 됩니다. 전체 예제를 살펴봅시다.

 

⭐️ UnivStudentInheri.cpp

 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <cstring>
using namespace std;
 
class Person{
private:
    int age;        // 나이
    char name[50];  // 이름
public:
    Person(int myage, char * myname) : age(myage){
        strcpy(name, myname);
    }
 
    void WhatsYourName() const{
        cout << "My name is " << name << endl;
    }
 
    void HowOldAreYou() const{
        cout << "I'm " << age << " years old" << endl;
    }
};
 
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{
        WhatsYourName();
        HowOldAreYou();
        cout<<"My major is " << major << endl << endl;
    }
};
 
int main(void){
    UnivStudent ustd1("Lee"22"Computer end.");
    ustd1.WhoAreYou();
 
    UnivStudent ustd2("Yoon"21"Electronic eng.");
    ustd2.WhoAreYou();
    return 0;
}
cs

 

 

⭐️ UnivStudentInheri.cpp 실행 결과

 

1
2
3
4
5
6
7
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.
cs


🌼유도 클래스의 객체 생성과정

아래 코드를 살펴보도록 합시다. 여기서 중요한 것은 기초 클래스의 생성자 호출입니다.

 

⭐️DerivCreOrder.cpp

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
using namespace std;
 
class SoBase{
private:
    int baseNum;
public:
    SoBase() : baseNum(20){
        cout << "SoBase()"<<endl;
    }
    SoBase(int n) : baseNum(n){
        cout<<"SoBase(int n)" << endl;
    }
    void ShowBaseData(){
        cout << baseNum<<endl;
    }
 
class SoDerived : public SoBase{
private:
    int derivNum;
public:
    SoDerived():derivNum(30){
        cout << "SoDerived()"<<endl;
    }
    SoDerived(int n) : derivNum(n){
        cout << "SoDerived(int n)" << endl;
    }
    SoDerived(int n1, int n2) : SoBase(n1), derivNum(n2){       // 생성자를 직접 명시
        cout << "SoDerived(int n1, int n2)" << endl;
    }
    void ShowDerivData(){
        ShowBaseData();
        cout << derivNum << endl;
    }
};
 
int main(void){
    cout << "case1....." << endl;
    SoDerived dr1;
    dr1.ShowDerivData();
    cout << "--------------" << endl;
    cout << "case2..... " << endl;
    SoDerived dr2(12);
    dr2.ShowDerivData();
    cout << "--------------" << endl;
    cout << "case3..... " << endl;
    SoDerived dr3(2324);
    dr3.ShowDerivData();
    return 0;
}
cs

 

⭐️DerivCreOrder.cpp 실행 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
case1.....
SoBase()
SoDerived()
20
30
--------------
case2..... 
SoBase()
SoDerived(int n)
20
12
--------------
case3..... 
SoBase(int n)
SoDerived(int n1, int n2)
23
24
 
cs

 

소스코드를 살펴보면 다음과 같은 사실을 알 수 있습니다.

💡 유도 클래스의 객체생성 과정에서 기초 클래스의 생성자는 100% 호출된다.

💡 유도 클래스의 생성자에서 기초 클래스의 생성자 호출을 명시하지 않으면 기초 클래스의 void 생성자가 호출된다.

 

🌼 유도 클래스 객체의 소멸과정

유도 클래스의 객체 생성과정에서는 생성자가 두 번 호출됨을 알았습니다. 이와 비슷하게 소멸 과정에서도 두 번 소멸자가 호출될 것을 유추할 수 있습니다. 아래 예제를 통해 내용을확인해보도록 합시다.

 

⭐️ DerivDestOrder.cpp

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
31
32
#include <iostream>
using namespace std;
 
class SoBase{
private:
    int baseNum;
public:
    SoBase(int n) : baseNum(n){
        cout << "SoBase(): " << baseNum << endl;
    }
    ~SoBase(){
        cout << "~SoBase() : " << baseNum << endl;
    }
};
 
class SoDerived : public SoBase{
private:
    int derivNum;
public:
    SoDerived(int n) : SoBase(n), derivNum(n){
        cout << "SoDerived(): " << derivNum << endl;
    }
    ~SoDerived(){
        cout << "~SoDerived() : " << derivNum << endl;
    }
};
 
int main(void){
    SoDerived dr1(15);
    SoDerived dr2(27);
    return 0;
}
cs

 

⭐️ DerivDestOrder.cpp 실행 결과

1
2
3
4
5
6
7
8
SoBase(): 15
SoDerived(): 15
SoBase(): 27
SoDerived(): 27
~SoDerived() : 27
~SoBase() : 27
~SoDerived() : 15
~SoBase() : 15
cs

 

위 코드를 통해 다음과 같은 사실을 알 수 있습니다.

💡 유도 클래스의 객체가 소멸될 때에는, 유도 클래스의 소멸자가 실행되고 난 다음에 기초 클래스의 소멸자가 실행된다.

💡 스택에서 생성된 객체의 소멸순서는 생성순서와 반대이다.

 

유도 클래스의 생성자 및 소멸자 정의의 모델을 제시하는 코드를 살펴봅시다. 

 

⭐️ DestModel.cpp

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <cstring>
using namespace std;
 
class Person{
private:
    char * name;
public:
    Person(char * myname){
        name = new char[strlen(myname) + 1];
        strcpy(name, myname);
    }
    ~Person(){
        delete []name;
    }
    void WhatYourName() const{
        cout << "My name is " << name << endl;
    }
};
 
class UnivStudent : public Person{
private:
    char * major;
public:
    UnivStudent(char * myname, char * mymajor) : Person(myname){
        major = new char[strlen(mymajor) + 1];
        strcpy(major, mymajor);
    }
    ~UnivStudent(){
        delete []major;
    }
    void WhoAreYou() const{
        WhatYourName();
        cout << "My major is " << major << endl << endl;
    }
};
 
int main(void){
    UnivStudent st1("Kim""Mathmatics");
    st1.WhoAreYou();
    UnivStudent st2("Hong""Physics");
    st2.WhoAreYou();
    return 0;
}
cs

⭐️ DestModel.cpp 실행결과

1
2
3
4
5
My name is Kim
My major is Mathmatics
 
My name is Hong
My major is Physics
cs

 

📜 출처

윤성우(2010). 윤성우 열혈 C++ 프로그래밍. 오렌지미디어.

Comments