상속을 하는 2가지 – 일반화, 특수화
일반화
- 기획단계에서 상속을 사용할지 말지 결정한다.
- 초반에 보고 상속을 하면 되겠다.
- 작업하다 보니 상속을 하면 되겠다.
- 하고 상속을 판단하여 구조를 짜는 경우
특수화
- 개발과정에서 상속을 사용할지 말지 경정한다.
- SRP – 클래스는 한 가지의 기능 책임을 지는 것이다.
- 근데 개발하다 보니 클래스가 너무 비대해진다.
- 이것을 기능 별로 쪼개서 분리하고 상속하는 구조를 짜는 경우
상속 (특수화) 실습
상황 1.
– 기획자가 등장 동물 - 3가지밖에 없다고 단정 / 클래스 하나로 다 만드는 것이 났겠다 판단
Animal 클래스 하나에 돼지, 소, 닭 - 다 만들 것이다.
#include <iostream>
#include <string>
using namespace std;
class Animal {
private:
string _name;
int _type;
float _age;
float _weight;
float _height;
bool _isFly;
public:
Animal(string name, int type, float age, float weight, float height, bool isFly, bool isSwim)
:_name(name), _type(type), _age(age), _weight(weight), _height(height), _isFly(isFly), _isSwim(isSwim) {}
~Animal() {}
void Speak() {
switch (_type) {
case 0:
cout << _name << "가 꿀꿀 합니다." << endl;
break;
case 1:
cout << _name << "가 음매 합니다." << endl;
break;
case 2:
cout << _name << "가 꼬끼오 합니다." << endl;
break;
}
}
void Run() {
if (_isFly) {
cout << _name << "이 납니다" << endl;
}
else {
cout << _name << "이 뜁니다" << endl;
}
}
void Eat() {
cout << _name << "이 먹습니다." << endl;
}
};
1- type; : 이 클래스로 돼지, 소, 닭, 소, 만들어 줄 것이므로 타입 필요
2- 필요한 멤버 변수들 해주고.
3- 생성자/ 소멸자 생성
4- 객체별로 다르게 적용되는 함수 필요– 분기문으로
4.1 – 돼지, 소, 닭 - 다르게 말한다. - 각각 speak( ) 다르게 적용 필요
4.2 - 돼지, 소, 닭 - 뛰거나 난다 - 각각 run( ) 다르게 적용 필요
5- main에서 객체 생성한 뒤 함수 호출해서 test
int main() {
Animal pig("pig", 0, 1.2f, 200.0f, 100.0f, false, false);
Animal cow("cow", 1, 2.3f, 300.0f, 170.0f, false, false);
Animal noFlyChicken("noFlyChicken", 2, 0.8f, 3.0f, 30.0f, false, false);
Animal flyChicken("FlyChicken", 2, 0.8f, 3.0f, 30.0f, true, false);
pig.Speak();
cow.Speak();
noFlyChicken.Speak();
flyChicken.Speak();
cout << endl;
pig.Run();
cow.Run();
noFlyChicken.Run();
flyChicken.Run();
return 0;
}
정상출력 :D
상황 1 종료!
상황 2
- 이렇게 다해놨는데, 기획자가 와서 요새 애들이 돌고래 좋아한다고 추가해달라고 한다..
돌고래 추가한다...
#include <iostream>
#include <string>
using namespace std;
class Animal {
private:
string _name;
int _type;
float _age;
float _weight;
float _height;
bool _isFly;
bool _isSwim;
public:
Animal(string name, int type, float age, float weight, float height, bool isFly, bool isSwim)
:_name(name), _type(type), _age(age), _weight(weight), _height(height), _isFly(isFly), _isSwim(isSwim) {}
~Animal() {}
void Speak() {
switch (_type) {
case 0:
cout << _name << "가 꿀꿀 합니다." << endl;
break;
case 1:
cout << _name << "가 음매 합니다." << endl;
break;
case 2:
cout << _name << "가 꼬끼오 합니다." << endl;
break;
case 3:
cout << _name << "가 끽끽 합니다." << endl;
break;
}
}
void Run() {
if (_isFly) {
cout << _name << "이 납니다" << endl;
}
else {
if (_isSwim) {
cout << _name << "이 헤엄칩니다." << endl;
}
else {
cout << _name << "이 뜁니다" << endl;
}
}
}
void Eat() {
cout << _name << "이 먹습니다." << endl;
}
};
int main() {
Animal pig("pig", 0, 1.2f, 200.0f, 100.0f, false, false);
Animal cow("cow", 1, 2.3f, 300.0f, 170.0f, false, false);
Animal noFlyChicken("noFlyChicken", 2, 0.8f, 3.0f, 30.0f, false, false);
Animal flyChicken("FlyChicken", 2, 0.8f, 3.0f, 30.0f, true, false);
Animal dolphin("dolphin", 3, 0.8f, 3.0f, 30.0f, false, true);
pig.Speak();
cow.Speak();
noFlyChicken.Speak();
flyChicken.Speak();
dolphin.Speak();
cout << endl;
pig.Run();
cow.Run();
noFlyChicken.Run();
flyChicken.Run();
dolphin.Run();
return 0;
}
1 - 돌고래 타입 추가해준다.
2 - 수영하는지 안 하는지 멤버 변수 추가
3 - 체크하기 위해 생성자 초기화 리스트 뒤에 추가
4 - speak( ) 부분에 case 추가
5 - run( ) 부분 if - else 조건 수정
6 - 객체 생성 후 테스트
돌고래 추가 완료 : 상황 2 종료!
상황 3
– 이렇게 다해놨는데, 기획자가 와서 둘리가 카메오로 등장하게 해 달라 요청
이런 식으로 이질적인 애가 자꾸 추가되게 되면,
걔가 사용하는 멤버 변수도,, 생성자도, 함수도, 분기문도, 다 추가 및 수정해줘야 한다..
이럴 경우 - 상속 사용할지 생각 필요
이럴 때 사용하는 상속이 : 특수화 상속!
상속 사용하기로 판단 - 상속(특수화) 시작
분리 시작
1. type을 없애는 것에 초점이 맞춰질 것이다.
2. 일반적인 부분은 놔두고 - Pig, Cow, Chicken, Dolphin 분리
3. 빼낸 Class 부분 - 생성자/함수 수정 ,
- 일반적인 거는 부모로 넘기고 특수한 조건은 - 자기 클래스에서 추가, 수정(Speak() , isFly, isSwim 같은)
4. Animal 클래스 부분- 뺄꺼빼주고 남은 것들 중 일반적인 것들만 남겨놓고 삭제.
- 다 빼줬으면 일반적인 내용으로 수정 ( Speak ( ) 부분 - 음매/꼬끼오/끼끽 빼고 -> 말합니다로 초기화 등)
5. main( ) 부분 테스트 – 객체 생성 부분 수정
- ( Animal Pig ( ) 애니멀 타입의 pig 가 아닌 -> Pig pig로! )
#include <iostream>
#include <string>
using namespace std;
//부모 클래스
class Animal {
protected:
string _name;
float _age;
float _weight;
float _height;
public:
Animal(string name, float age, float weight, float height)
:_name(name), _age(age), _weight(weight), _height(height) {}
~Animal() {}
void Speak() {
cout << _name << "가 말합니다." << endl;
}
void Run() {
cout << _name << "이 뜁니다" << endl;
}
void Eat() {
cout << _name << "이 먹습니다." << endl;
}
};
//돼지
class Pig : public Animal{
public:
Pig(string name, float age, float weight, float height)
: Animal(name, age,weight,height) {}
~Pig() {}
void Speak() {
cout << _name << "가 꿀꿀 합니다." << endl;
}
};
//소
class Cow : public Animal{
public:
Cow(string name, float age, float weight, float height)
: Animal(name, age, weight, height) {}
~Cow() {}
void Speak() {
cout << _name << "가 음매 합니다." << endl;
}
};
//닭
class Chicken : public Animal{
private:
bool _isFly;
public:
Chicken(string name, float age, float weight, float height, bool isFly)
// ,_isFly(isFly) 해주는 부분 체크
: Animal(name, age, weight, height), _isFly(isFly) {}
~Chicken() {}
void Speak() {
cout << _name << "가 꼬끼오 합니다." << endl;
}
void Run() {
if (_isFly) {
cout << _name << "이 납니다" << endl;
}
else {
cout << _name << "이 뜁니다" << endl;
}
}
};
//돌고래
class Dolphin : public Animal {
public:
Dolphin(string name, float age, float weight, float height)
: Animal(name, age, weight, height) {}
~Dolphin() {}
void Speak() {
cout << _name << "가 끽끽 합니다." << endl;
}
//Run 부분, 생성자 부분 치킨이랑 다른 점 체크 -
void Run() {
Swim();
}
private:
void Swim() {
cout << _name << "이 헤엄칩니다." << endl;
}
};
int main() {
Pig pig("pig", 1.2f, 200.0f, 100.0f);
Cow cow("cow", 2.3f, 300.0f, 170.0f);
Chicken noFlyChicken("noFlyChicken", 0.8f, 3.0f, 30.0f, false);
Chicken flyChicken("FlyChicken", 0.8f, 3.0f, 30.0f, true);
Dolphin dolphin("dolphin", 0.8f, 3.0f, 30.0f);
pig.Speak();
cow.Speak();
noFlyChicken.Speak();
flyChicken.Speak();
dolphin.Speak();
cout << endl;
pig.Run();
cow.Run();
noFlyChicken.Run();
flyChicken.Run();
dolphin.Run();
return 0;
}
상속은 도구다.
공통된 내용을 뽑아내서 클래스를 만들고 상속시키는 것
- 상속 일반화 – 구조작업 때 주로 사용
클래스를 만들었는데, 클래스 하나가 여러 기능을 하려고 하니까type을 쓰고 분기문이 추가된다.
복잡해지면서 비효율적이 되고,, 크기가 커진다
공통된 부분 빼내서 독립 - 상속 특수화
일반적인애 말고 특수한애들 뽑아내서 상속화 – 리팩토링
+ Check Point
▣ 상속은 여러 번 사용 가능하다. - 상속 클래스마다 소멸자 필요하다.
▣ 상속받은 클래스로 객체를 만들면 -> 생성자, 소멸자 생기는 순서 check
- 부모 생성장 만들어지고, 자식 생성자
- 자식 소멸자 만들어지고, 부모 소멸자
▣ 이전 개발자가 만들어놓은 클래스가 있고, 이미 이 클래스를 여러 곳에서 많이 사용하고 있는 경우 - 기능 추가
- 기존의 클래스 자체를 수정할 수 없다. - 그런데 기능이 아쉽다. ( e.g 나누기가 없다 )
- 이럴 경우 상속을 통해 기능을 확장 생각해볼 수 있다.
#includ <iostream>
using namespace std;
class Math {
public:
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}
int mul(int a, int b) {
return a * b;
}
};
Math 데이터 타입으로 객체를 만들면 3개밖에 못쓴다 ( 나누기 없다 )
나누기 기능 추가 – Math 상속받은 ExtMath를 만들어 준다.
Math 가 아닌 Math를 상속받아서 기능 추가한 ExtMath로
- - Math의 기능 3가지에 ExtMath 기능 나누기 추가해서 4개 기능 사용 가능!
- - 이처럼 기능 추가의 목적으로 상속을 사용하기도 한다!
기능 추가 개념의 상속 한 번 더- string 클래스의 기능 추가하기
기본 제공 string 클래스의 기능을 이어받으면서 -( 상속받으면서 )
string의 문자열 개수를 세어주는 기능 추가 함수 만들기
String 클래스의 기본 개념 잡고 가기
▣ String class에 만들어진 소스를 변경하거나 추가하는 것이 불가능
▣ 문자열 저장이라는 것은 동적으로 돌아가는 것.
- string a(“monster”); 했을 경우
- a 객체에는 monster가 들어있는 것이 아니라 "monster" 저장되어있는 공간의 선두 번지 주소 값이 있는 것이다.
▣ c_str( ) ----> string에 있는 함수 - 문자열이 저장되어있는 배열의 선두 번지 주소 값 받아오는 함수
▣ 문자를 저장하는 마지막 배열에는 특수문자 '\0' 가 있다 - 널 종단 분자 - 마지막 부분이라는 것 표시해준다.
6번 Line - string 상속받은 extString 클래스 생성
8번 Line - 인자로 받은 문자 배열의 주소 값을 부모 string( )으로 전달
11번 Line - extString 클래스의 추가 기능 구현
15번 Line - *str++ ( *포인터 연산자와, 후위 연산이라는 점 check )
- string 문자열 있으면 count++해주고 ( 후위 연산 - 주소 값++ 의 의미는 다음칸으로 이동) *str++
- 이렇게 반복하다가 - 문자 배열의 마지막 0 만나면 거짓으로 while 빠져나오고 count 출력
review
상속 특수화에 대한 내용을 실습을 통해 알아보았다.
실제 상황?을 예시로 들어서 진행한 부분이
이제 나도 개발자로 일을 하게 되면 저렇겠구나
느껴져서 더 집중할 수 있었다.
개발을 하다 보니 새로 들어오는 요청사항이나 추가되는 기능들로 인해
하나의 클래스에서 너무 많은 기능을 책임지고 있다.
너무 비대해진 클래스의 기능을 따로 빼내서
그것을 상속받는 구조로 만들어 주는 것이 - 특수화 상속!
내가 알던 상속의 개념이었던 일반화와 다른 상속 특수화 새로웠다 ㅎㅎ
- 개발하는 과정 중에 비대해진 클래스의 기능을 분리하기 위한 상속
- 이미 너무 많은 곳에서 사용하고 있는 클래스를 수정할 수 없으니까
그 클래스를 상속받아서 기능을 추가하는 기능 추가적 상속
개발자가 실제 사용하는 상속의 경우인 것 같아서 재미있었다.
## Check Point --- c_str( ) 부분 , *str++ 부분
'Back-end > C++' 카테고리의 다른 글
22. 03. 28 - 포함 ( Compositon, Aggregation ) (0) | 2022.04.06 |
---|---|
22. 03. 25 - 상속( 접근 제어자, 업 캐스팅, 다운 캐스팅 ) (0) | 2022.04.03 |
22. 03. 23 - 캡슐화, 상속(일반화) : 동물 육성 게임 (0) | 2022.04.02 |
22. 03. 22 - 파일 분할, inline 함수 (0) | 2022.04.02 |
22.03.21. 복사 생성자, const함수 (0) | 2022.04.02 |