상속에 대한 수업을 들었다.
1. 부모 기능 보장 및 확장
1.1 코드 설명
아래 코드는 C_CHILD 클래스가 C_PARENT 클래스를 상속받고 있으며, C_CHILD 클래스의 test 메서드에서 부모 클래스의 test 기능을 호출하여 부모 기능을 보장하는 동시에 자식의 고유 기능도 확장하는 예제이다.
// parent.h
#pragma once
#include <stdio.h>
class C_PARENT
{
public:
virtual ~C_PARENT() = default;
virtual void test();
};
// child.h
#pragma once
#include"parent.h"
class C_CHILD : public C_PARENT
{
public:
void test() override;
};
// parent.cpp
#include "parent.h"
void C_PARENT::test()
{
printf("부모 기능\n");
}
// child.cpp
#include "child.h"
void C_CHILD::test()
{
C_PARENT::test();
printf("자식 기능\n");
}
// main.cpp
#include <iostream>
#include "child.h"
int main()
{
C_PARENT* pData{};
pData = new C_CHILD{};
pData->test();
}
1.2 수정된 부분 설명
C_PARENT::test();
- C_CHILD::test에서 C_PARENT::test()를 호출하여 부모 클래스의 기능을 실행한 후 자식의 고유 기능을 추가로 실행하게 했다.
- 이렇게 하면 부모 클래스의 기능을 유지하면서 자식 클래스의 기능을 확장한 것이 됩니다.
2. 상속의 공통된 부분 관리
2.1 코드 설명
아래는 I_ANIMAL이라는 인터페이스를 만들어 C_ANIMAL 클래스와 이를 상속하는 C_DOG 및 C_CAT 클래스를 정의한 코드이다. 여기서 각 동물 클래스는 move 메서드를 구현해 특정 동물의 움직임을 표현한 것이다.
// animal.h
#pragma once
#include <stdio.h>
__interface I_ANIMAL
{
void move();
};
class C_ANIMAL abstract : public I_ANIMAL
{
public:
C_ANIMAL() = default;
virtual ~C_ANIMAL() = default;
C_ANIMAL(const C_ANIMAL&) = default;
C_ANIMAL& operator=(const C_ANIMAL&) = default;
};
// dog.h
#pragma once
#include "animal.h"
class C_DOG : public C_ANIMAL
{
public:
void move() override;
};
// cat.h
#pragma once
#include "animal.h"
class C_CAT : public C_ANIMAL
{
public:
// C_ANIMAL을(를) 통해 상속됨
void move() override;
};
// dog.cpp
#include "dog.h"
void C_DOG::move()
{
printf("네발로 개가 걷는다\n");
}
// cat. cpp
#include "cat.h"
void C_CAT::move()
{
printf("네발로 고양이가 걷는다\n");
}
// main.cpp
#include <iostream>
#include "dog.h"
#include "cat.h"
int main()
{
C_ANIMAL* arData[5]{};
arData[0] = new C_DOG{};
arData[1] = new C_CAT{};
arData[2] = new C_DOG{};
arData[3] = new C_CAT{};
arData[4] = new C_DOG{};
for (int i = 0; i < 5; i++)
{
arData[i]->move();
}
}
2.2 수정된 부분 설명
- 여기서 주의 깊게 봐야 하는 부분은 "네발로"의 부분이다.
(주의점 : 만들다 보니 공통된 부분이 생긴 것이지, 공통된 부분을 관리하려고 상속을 받은 것이 아니다.) - C_ANIMAL::move() 메서드에 "네발로"라는 공통된 기능을 구현, 이를 통해 C_DOG, C_CAT 클래스에서 호출하도록 했다.
- 이 방식은 부모 기능을 보장하면서 확장하는 개념으로, 순수 가상 함수(인터페이스)와 구현을 분리해 공통 기능을 적절히 사용할 수 있게 해 준다.
2.3 주의 사항
- 모든 동물에게 공통된 동작이 존재하는 것은 아니다. 예를 들어 물고기와 같은 다른 동물들은 다르게 움직이기 때문에 반드시 공통 기능이 필요한 경우 사용하는 것이 좋다.
- 이름이 똑같고 네임스페이스를 사용한다 라면 상속이고 부모를 보장하면서 확장한다는 것을 보고 알아야 한다.
// animal.h
#pragma once
#include <stdio.h>
__interface I_ANIMAL
{
void move();
};
class C_ANIMAL abstract : public I_ANIMAL
{
public:
C_ANIMAL() = default;
virtual ~C_ANIMAL() = default;
C_ANIMAL(const C_ANIMAL&) = default;
C_ANIMAL& operator=(const C_ANIMAL&) = default;
virtual void move();
};
// animal.cpp
#include "animal.h"
void C_ANIMAL::move()
{
printf("네발로 ");
}
// dog.cpp
#include "dog.h"
void C_DOG::move()
{
C_ANIMAL::move();
printf("개가 걷는다\n");
}
// cat.cpp
#include "cat.h"
void C_CAT::move()
{
C_ANIMAL::move();
printf("고양이가 걷는다\n");
}
3. 상속 방법론
3.1 설명
상속을 언제 사용하는지에 대한 2가지 중요 개념이 존재한다.
- 유스케이스 이론 : 상속을 통해 특정 객체의 본질과 사용법을 정의한다.
- 동사의 확장 : 상속은 기능(동사)만 상속받아야 하며, 명사(예: 특정 종류의 행동이나 사물)와 같은 구체적 개념을 상속을 하면 안 된다.
3.2 예시 코드
예시로 C_DOCK 클래스를 상속받아 오리가 걸어 다니거나 수영하고, 날아다니는 여러 방식의 오리 클래스가 정의한다.
// dock.h
#pragma once
#include <stdio.h>
__interface I_DOCK
{
void move();
};
class C_DOCK : public I_DOCK
{
public:
C_DOCK() = default;
virtual ~C_DOCK() = default;
C_DOCK(const C_DOCK&) = default;
C_DOCK& operator=(const C_DOCK&) = default;
};
// flydock.h
#pragma once
#include "dock.h"
class C_FLYDOCK : public C_DOCK
{
public:
void move() override;
};
// swimdock.h
#pragma once
#include "dock.h"
class C_SWIMDOCK : public C_DOCK
{
public:
void move() override;
};
// walkdock.h
#pragma once
#include "dock.h"
class C_WALKDOCK: public C_DOCK
{
public:
void move() override;
};
// flydock.cpp
#include "flydock.h"
void C_FLYDOCK::move()
{
printf("날아다니는 오리\n");
}
// swimdock.cpp
#include "swimdock.h"
void C_SWIMDOCK::move()
{
printf("수영하는 오리\n");
}
// walkdock.cpp
#include "walkdock.h"
void C_WALKDOCK::move()
{
printf("걸어다니는 오리\n");
}
// main.cpp
#include <iostream>
#include "flydock.h"
#include "swimdock.h"
#include "walkdock.h"
int main()
{
C_DOCK* pDock[3]{};
pDock[0] = new C_FLYDOCK{};
pDock[1] = new C_SWIMDOCK{};
pDock[2] = new C_WALKDOCK{};
for (int i = 0; i < 3; i++)
{
pDock[i]->move();
}
}
이 예시에서 걸어 다니는 오리, 수영하는 오리, 날아다니는 오리 각각의 move 메서드가 다르게 구현되어 있다.
만약 여기서 소리 기능 (음악, 멜로디, 꽥소리) 3가지 기능이 추가된다고 하면 몇 개의 클래스를 만들어야 할까?
답은 9개이다. 이유는 기존 상속에 있던 것에 새로 추가되는 기능을 곱해서 이 값이 나오게 된다.
즉 기능을 추가하게 되면 클래스가 급격히 늘어날 수 있는 것이다.
그렇기에 기능 분리 원칙에 따라 기능을 기준으로 잡고 상속을 받지 말아야 한다.
기능 분리 원칙에 따라 동사만 상속받고 기능을 조합하는 방식으로 해결 가능하다.
'C++' 카테고리의 다른 글
[강의] 11월 15일 수업정리 (0) | 2024.11.17 |
---|---|
[강의] 11월 14일 수업정리 (8) | 2024.11.15 |
[강의] 11월 12일 수업정리 (6) | 2024.11.13 |
[강의] 11월 8일 수업정리 (1) | 2024.11.10 |
[강의] 11월 7일 수업정리 (1) | 2024.11.08 |