다형성

킴스코딩클럽·2022년 12월 7일
1

CS기초 시리즈

목록 보기
60/71

다형성

상황에 따라 다양한 형태로 해석가능

캐릭터의 충돌을 표현해보자

class Warrior;
class Archer;
class Wizard;
class Paladin;

bool CheckCollision(Warrior A, Archer B);
bool CheckCollision(Paladin A, Archer B);
...
캐릭터가 다양할 수록 만들어야하는 함수가 기하급수적으로 늘어남

같은 부모를 가지고 있다면
class Warrior: public Character;
class Archer :public Character;
class Wizard :public Character;
class Paladin:public Character;

자식은 부모다가 가능함
(child is a parent)
bool CheckCollision(Character,Character);
위저드 아처 팔라딘 등을 캐릭터로 할 수 있다면 쉬워짐

상속과 포인터(필수), virtual(가상 멤버 함수)으로 다형성을 표현할 수 있음

A* p = new C;
C는 상황에따라 C의 부모로 취급 가능함

virtual member function

자식에서 재정의(override)할 것을 기대하는 가상의 멤버함수
가상임으로 실제로 존재하는 것이 아니라 누군가가 실체를 제공해줌
자식으로 해석하고싶은 것

class character
{
public:
	virtual void attack()
	{
		std::cout << "공격" << std::endl;
	}

};

class warrior : public character
{
public:
	void attack() override
	//오버라이드를 명시하게 되어있음
	//modern c++이후의 기능
	//안써도되지만 오버라이딩과 관련된 필수기능이기 때문
	//가상함수를 오버라이딩한다는 뜻으로 쓸 수 있음

	{
		std::cout << "칼로 공격" << std::endl;
	}
};

class wizard : public character
{
public:
	void attack()
	{
		std::cout << "마법 공격" << std::endl;
	}
};

class archer : public character
{
public:
	void attack()
	{
		std::cout << "활로 공격" << std::endl;
	}
};

class thief : public character
{
public:
	void attack()
	{
		std::cout << "돈을 훔침" << std::endl;
	}
};

class prist : public character
{
public:
	void attack()
	{
		std::cout << "회복 주문" << std::endl;
	}
};

int main()
{

		character *c = new warrior;
	c->attack();	
	//전사가 되어야 하지만 아님
	//워리어로 만들었지만 포인터 자체가 캐릭터이기 때문에
	//하지만 워리어의 함수로 만들고 싶음

	character *party[5] {
		new warrior,
		new wizard,
		new archer,
		new prist,
		new thief
	};

	//공격을 시켜보면

	for ( int i = 0; i < 5; i++ )
	{
		party[i]->attack();
		//편한데 전부다 부모의 공격을 사용할 수 밖에없음
	}
	//캐릭터포인터로 가리키더라도 만든 클래스의 기능을 쓰고 싶음
	//이 때 부모의 attack함수를 virtual로 만들면
	//실체가 없으므로 실제 객체들에서 찾아서 하려고 할 것
	//그래서 객체별로 다른 공격을 하게됨
	//기능은 있지만 실제는 자식 객체를 참조하세요 라는 뜻
	//상황에따라 다르게 불림
	//이것이 다형성
	//상속의 관계가 있어야만 다형성이 가능해짐
	//명시적으로 인스턴스를 만들면 못씀(명시적임으로)
	//상황에 따라 가리킬수있으므로 포인터만 가능함
	//거기에 virtual 기능이 상황에 따라서 가능하도록 만들어주는 것이 이 키워드임
	//만약에 warrior를 재상속 받는 클래스가 있다면
	//워리어와 캐릭터 모두 virtual attack이어야함

	character *pchar = new warrior;
	pchar->attack();

	warrior *w = (warrior *) p;

	for ( int i = 0; i < 5; i++ )
	{
		warrior *pw = (warrior*)(party[i]);
		pw->attack();
	}
    
}

Binding

early(static,compiletime) binding vs late(dynamic, runtime) binding

가상함수의 경우는 late binding(다형성에 의해서 실체를 다양하게 해석해야하기 때문)
프로그램이 실행될 때 누구에게 바인딩 할 것인지 결정하게됨
실제로 이런 기능을 코드로 구현하기도 함

virtual 소멸자

부모의 소멸자는 반드시 virtual(가상) 소멸자로 만들어야함
가상 소멸자로 만들지 않으면 memory leak 가능성이 있음

다형성과 형변환

암시적 형변환 : 데이터 손실이 적은 쪽으로 일어날 때

float x= (float)4/2.0f;
상향 변환(Up Cast)
자식에서 부모로 가는 것이 가능함

명시적 형변환 : 데이터가 손실될 수 있어 조심해야함

부모에서 자식
하향 변환(down cast)
프로그래머가 결과를 가져감(컴파일러는 컴파일해주지만 문제 가능성)

다형성 -> runtime에 발생

형변환은 런타임에 하는 경우가 생김

형변환에서 고려해야할 사항

1.데이터 손실
2.상향이냐 하향이냐
3.런타임이냐 컴파일타임이냐

c++ 스타일의 형변환

static_cast(early binding, compile time)

c언어 스타일의 형변환 + 추가 기능

dynamiccast_(late binding, run-time)

동적으로 객체를 파악함
실행한다음 다형성하듯 메모리에서 p의 정체를 찾아냄

reinterpret_cast
const_cast

c스타일의 형변환은 좋지 않음 위의 2가지 형변환을 사용하기(객체 개념 반영 못함)

pure virtual

virtual : 자식이 재정의하길 기대하는 함수 (강제성이 없음)
강제성이 있도록 하는 기능이 필요

pure virtual : 자식이 반드시 재정의해야 하는 것

Abstract Class(추상 클래스)

추상적인 개념 - 규격이나 법칙과 같은 개념으로 강제성을 부여함
인스턴스화가 불가능함
순수 가상 함수를 하나라도 가지고 있는 클래스
자식들은 반드시 순수가상함수를 오버라이딩해야만 함

interface

모든 멤버가 순수가상함수인 클래스
정보가 없고 함수의 규칙만 있는 함수

class HDMI
{
	public:
	virtual void Pins() =0;
    virtual int Width()	=0;
};
HDMI display;	//순수가상함수라서 인스턴스 불가능
class LgMoniter : public HDMI
{
	//상속을 받아서 밖에 사용 불가능함
    //LGMONITOR 고유의 기능을 만들어서 사용해야함
    //하지만 HDMI의 필수 규격은 지켜야함
    void Pins() 
    {    
    }
    int Width()
    {
    }
};
profile
공부 기록용

0개의 댓글