도순씨의 코딩일지
C++ :: 상속(Inheritance) 본문
🌼 상속의 개념
상속은 기존에 정의해 놓은 클래스의 재활용을 목적으로 합니다. 먼저 예제를 하나 살펴봅시다.
⭐️ 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(23, 24);
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
cs
⭐️ DestModel.cpp 실행결과
cs
📜 출처
윤성우(2010). 윤성우 열혈 C++ 프로그래밍. 오렌지미디어.
'𝐏𝐑𝐎𝐆𝐑𝐀𝐌𝐌𝐈𝐍𝐆 > 𝐂++' 카테고리의 다른 글
C++ :: 객체 포인터의 참고관계, 가상함수, 순수 가상함수, 가상 소멸자 (0) | 2020.08.27 |
---|---|
C++ :: protected 상속, private 상속, IS-A 관계, HAS-A 관계 (2) | 2020.08.26 |
C++ :: const, friend 키워드 (0) | 2020.08.16 |
C++ :: 복사 생성자(Copy Constructor) (0) | 2020.08.16 |
C++ :: 클래스와 배열, this 포인터 (0) | 2020.08.15 |