※ LIG Nex1 The SSEN Embedded SW School에서 진행된 내용을 정리한 포스팅입니다.
다형성
다형성
- 동일한 코드가 실행 방법에 따라 다르게 동작하기 위함이다.
- 함수 오버라이딩
- 상속받은 멤버 함수를 하위 클래스에 적합하게 재정의
- 함수의 프로토타입을 그대로 가져간다.
- 업/다운 캐스팅
- 상속 관계에서 사용하는 캐스팅
- 가상함수
- 재정의를 하더라도 실제 할당된 객체의 함수를 찾아가도록 하는 키워드
오버라이딩
- 상속 받는 객체의 함수가 적합하지 않은 경우 재정의를 통해 기능을 확장하거나 수정한다.
- 정적 호출(정적 바인딩)은 타입을 따라간다.
class Point {
protected:
int x;
int y;
public:
Point():x(0), y(0) {}
Point(int x, int y): x(x), y(y) {}
void printPoint() {
cout << "x: " << " y: " << y << endl;
}
};
class Point3D : public Point {
private:
int z;
public:
Point3D():z(0) {}
Point3D(int x, int y, int z): Point(x, y), z(z) {}
// overriding
printPoint() {
Point::printPoint(); // 부모의 멤버 함수를 명시적으로 호출
cout << "z: "<< z << '\n';
}
};
업/다운 캐스팅
- 상속 관계에서 하위 객체 주소를 상위 타입 포인터에 할당하는 것
- 가상함수가 아니라면 재정의 함수 호출은 포인터의 타입을 따라간다.
- 상위 클래스의 타입에 정의된 멤버만 사용 가능하다.
// up casting
Point *pp = &p;
pp->printPoint();
메서드의 오버로딩을 다형성을 활용하여 개선하기
Computer와 Tv, Audio 객체가 있고, 이를 구매하는 buyer 클래스가 각각의 클래스에 해당하는 buy 함수를 오버라이딩 한 상태의 소스코드를 개선해보자.
class: Computer, Tv, Audio
class: buyer
-> var: money, point
-> func:
buy(Tv tv);
buy(Computer computer);
buy(Audio audio);
class Appliance {};
class Computer: public Appliance {};
class Tv: public Appliance {};
class Audio: public Appliance {};
class Buyer {
int budget;
int point;
public:
Buyer();
void buy(Appliance &tar);
};
Appliance 라는 전체를 포괄하는 클래스로 하위 객체를 묶어 보일러 플레이트 코드를 개선할 수 있다.
int main(void) {
Buyer buyer;
Appliance *himart[3];
himart[0] = new Tv();
himart[1] = new Computer();
himart[2] = new Audio();
for (int i = 0; i < 10; ++i)
buyer.buy(*himart[rand()*10 % 3]);
for (int i = 0; i < 3; ++i)
delete himart[i];
return 0;
}
다운 캐스팅
- 업 캐스팅된 포인터를 원래 타입으로 캐스팅 하는 것.
- 캐스팅 연산자가 필요하다.
- 하위 클래스의 멤버에 접근해야 하는 경우 사용한다.
*((Point3D*) p).getZ();
- typeinfo 헤더의 typeid 함수를 활용하여 실제 할당된 객체의 클래스의 정보를 가져올 수 있다.
std::type_info - cppreference.com
The class type_info holds implementation-specific information about a type, including the name of the type and means to compare two types for equality or collating order. This is the class returned by the typeid operator. The type_info class is neither Cop
en.cppreference.com
Parent *p = new Parent(3);
Child *c = new Child(1, 2);
Parent *pc = c;
- typeid의 name을 출력하면 다음과 같은 값을 얻을 수 있다.
typeid(p): P6Parent
typeid(c): P5Child
typeid(pc): P6Parent
typeid(*p): 6Parent
typeid(*c): 5Child
typeid(*pc): 5Child
- 비교를 통해 실제 할당된 객체를 찾을 수 있다.
if (typeid(*pc) == typeid(Child))
cout << "pc is Child\n";
else if (typeid(*pc) == typeid(Parent))
cout << "pc is Parent\n";
가상 함수
- 함수 정의 시 앞에 virtual 키워드를 붙인다.
- 함수 호출 시 동적 바인딩을 통해 호출된다.
- 정적 바인딩은 타입을 따라간다.(컴파일 시 결정된다.)
- 동적 바인딩은 실제 생성된 객체를 찾아간다.
- 하나 이상의 가상함수를 만들면 가상함수 테이블(V-table)이 생성된다.
- 함수 호출 시 V-table을 참조하여 호출할 함수를 결정한다.(런타임 시 결정된다.)
순수 가상 함수
- 피카츄와 꼬부기가 상속 받고 있는 포켓몬 클래스를 생각해보자.
- 포켓몬 클래스는 객체를 생성하기 위해 존재하는 클래스가 아니다.
- 상속의 목적으로만 존재한다.
- 구현의 내용이 없음을 = 0 으로 명시한다.
virtual void eatSomthing() = 0;
추상 함수
- 구현문 없이 함수의 선언만 갖고 있는 함수
- 상속받은 하위 클래스에서 구현해야 한다.
- 인터페이스를 제공하는 것이 목적이다.
추상 클래스
- 추상 함수를 하나라도 갖고 있는 클래스
- 객체 생성이 불가능하다.
class PocketMon
{
public:
PocketMon();
PocketMon(string name, string img);
virtual void eatSomthing() = 0;
virtual void sleep() = 0;
virtual bool playAlone() = 0;
virtual bool doExercise() = 0;
virtual void lvCheck() = 0;
void printState();
};
※ 질문, 개선점, 오류가 있다면 댓글로 남겨주세요 :)
'Language > C++' 카테고리의 다른 글
[TheSSEN/C++] 10. 템플릿 (0) | 2025.01.29 |
---|---|
[TheSSEN/C++] 9. 연산자 오버로딩 (0) | 2025.01.27 |
[TheSSEN/C++] 7. 상속 (0) | 2025.01.27 |
[TheSSEN/C++] 6. static과 singleton 패턴 (0) | 2025.01.27 |
[TheSSEN/C++] 5. 객체 지향 프로그래밍 기법 (0) | 2025.01.27 |