본문 바로가기

언리얼 C++ 정리

09/15 학습 정리 ( 다중상속, Interface )

다중상속

 

다중상속의 예시인 다이아몬드문제(Diamond Problem)

 다중상속을 하면 C가 A에게 상속받은 변수를 사용하려고 해도 B1이 A에게 상속받은 변수인지 B2가 A에게 상속받은 변수인지 알수가 없기에 문제가 생긴다. 해결방법은 가상상속이 있으나 제일 좋은건 다중상속을 사용 안하는것이다.

 

  1. 여러 부모 클래스: 다중상속을 사용하면 하나의 자식 클래스가 여러 부모 클래스로부터 상속을 받을 수 있습니다. 이로 인해 자식 클래스는 여러 부모 클래스의 특성과 메서드를 모두 가질 수 있습니다.
  2. 다이아몬드 문제: 다중상속을 사용할 때 발생할 수 있는 문제 중 하나로 다이아몬드 문제(Diamond Problem)가 있습니다. 이 문제는 하나의 클래스가 두 개 이상의 부모 클래스로부터 상속받고, 이 부모 클래스들이 공통으로 같은 메서드나 속성을 가질 때 발생하는 모호성을 해결하는 문제입니다. 이러한 모호성을 해결하기 위해 언어마다 다른 방식의 규칙이나 해결 방법이 존재합니다.
  3. 복잡성 증가: 다중상속을 남용하면 코드의 복잡성이 증가할 수 있으며, 클래스 간의 관계를 이해하기 어려울 수 있습니다. 따라서 다중상속을 사용할 때는 신중하게 고려하고 설계해야 합니다.

인터페이스

 

 

C++에서 Interface를 부모클래스로 하는 PosableOneInterface생성후 GPCCharacter에서 사용을 할것인데 위의 사진처럼 BP에서 인터페이스 적용시키는걸 C++에서 적용시키는 것이다.

 

#include "PosableOneInterface.h" //인터페이스의 내용을 알아야하니 헤더파일 추가

UCLASS(Abstract) // GPCCharacter 클래스로 인스턴스를 생성 안할거니 UCLASS(Abstract)추가
class GPC_CPP_12_API AGPCCharacter:
	public ACharacter,
	public IPosableOneInterface
    //이때 인터페이스를 상속시켜 다중상속이 발생하지만 인터페이스의 내부함수들은 순수가상함수만
    //사용할것이라 오류가 발생하지않는다.
    
{
	
};

Interface생성

 

class GPC_CPP_12_API IPosableOneInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void BeginPoseA() PURE_VIRTUAL(IPosableOneInterface::BeginPoseA)
	virtual void BeginPoseB() PURE_VIRTUAL(IPosableOneInterface::BeginPoseB)
	virtual void EndPoseA() PURE_VIRTUAL(IPosableOneInterface::EndPoseA)
	virtual void EndPoseB() PURE_VIRTUAL(IPosableOneInterface::EndPoseB)
};

 

블루프린트에서는 인터페이스는 그저 알림같은 용도로 사용되고 인터페이스 자체로 기능을 구현할수없었는데 C++에서는 기본 구현도 가능해서 생성할때 h파일말고 cpp파일도 생성이 된다.

인터페이스는 순수가상함수를 사용해야 다중상속을 해도 문제가 없기에 순수가상함수만을 사용할것이다.

C++에서는 순수가상함수를 쓸때 함수명 뒤에 = 0;나 abstract를 붙여 사용하지만 언리얼에서는 이 방식을 사용할때

인스턴스 생성이 막히기에 PURE_VIRTUAL(클래스명::함수명)을 붙여 사용한다.

 

class GPC_CPP_12_API AGPCPlayerController:
	public APlayerController,
	public IPosableOneInterface
{
	GENERATED_BODY()
	
protected:
	void SetupInputComponent() override;

#pragma region IPosableOneInterface //#pragma region 이름으로 영역을 만들수있고 endregion으로 닫는다.
private:
	void BeginPoseA() override;
	void BeginPoseB() override;
	void EndPoseA()	override;
	void EndPoseB()	override;
#pragma endregion
};

 

PosableOneInterface에서 가져온 함수들을 오버라이드 시킨다.


Interface 정의

 

void AGPCPlayerController::BeginPoseA()
{
	if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
		PosableOne->BeginPoseA();
}

void AGPCPlayerController::BeginPoseB()
{
	if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
		PosableOne->BeginPoseB();
}

void AGPCPlayerController::EndPoseA()
{
	if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
		PosableOne->EndPoseA();
}

void AGPCPlayerController::EndPoseB()
{
	if (auto const PosableOne = Cast<IPosableOneInterface>(GetPawn()))
		PosableOne->EndPoseB();
}

//GPCPlayerController.cpp

 

함수를 정의하고 SetupInputComponet에서 Move,Look처럼 바인드 시켜준다. 액션축이니 BindAction 사용

 

InputComponent->BindAction("PoseA", EInputEvent::IE_Pressed, this, &ThisClass::BeginPoseA);
InputComponent->BindAction("PoseB", EInputEvent::IE_Pressed, this, &ThisClass::BeginPoseB);
InputComponent->BindAction("PoseA", EInputEvent::IE_Released, this, &ThisClass::EndPoseA);
InputComponent->BindAction("PoseB", EInputEvent::IE_Released, this, &ThisClass::EndPoseB);