TRPG 개발 중 캐릭터의 직업클래스와 부모 클래스를 만들면서 사용할 일이 있어서 알아봤다.
C++에서는 java처럼 abstact같은 키워드가 없다.
순수가상함수가 포함되어 있는 클래스는 추상클래스로 취급되고 순수가상함수는 상속받는 자식클래스에서 반드시 구현해야한다.
순수 가상 함수는 부모의 강제 규칙, 오버라이딩은 자식의 실제 구현.
class Job
{
public:
virtual Stat SetBaseState() const = 0;
virtual std::string GetJobName() const = 0;
}
class Warrior : public Job
{
Stat SetBaseState() const override
{
...구현...
}
std::string GetJobName() const override
{
...구현...
}
}
사실 class는 뭐 HttpClient 구현체 만들때나 쓰고 일부 백엔드 작업하면서나 써봤지 알고는 있지만 쓸일이 그닥 많지는 않았다.
어렵게 생각하지말고 내 입장에서는
추상클래스 = interface 라고 생각하면 기초수준에서의 이해는 되는 것 같다.
다만 다른점은 C++ 추상클래스는 클래스가 구현을 포함할 수 있고 TS interface는 구현을 위한 명세만을 다룬다는 점이다.
또한 TS interface는 ? 를 사용해 해당 함수가 상속받는 클래스 내부에 존재할수도 존재하지 않을 수도 있는 상태를 만들 수 있는데
C++에는 그런 방법이 없고 SOLID 원칙에서 안티패턴으로 간주하고 리팩토링을 하는 것을 권장하고있다.
#pragma once
#include <functional>
#include <map>
#include <string>
#include "Actor/Stat.h"
enum class JobState { Warrior, Mage };
class Job
{
std::map<char, std::function<void()>> jobFuncMap;
public:
virtual std::string Test();
virtual Stat SetBaseStat() const = 0;
virtual std::string GetJobName() const = 0;
};
순수가상함수는 구현할 필요가 없습니다. 왜냐?
자식클래스에서 반드시 구현해야 하기 때문에 추상클래스에서 구현할 필요가 없습니다.
하지만 일반 가상함수는 컴파일러가 어딘가에 구현이 있다고 생각하고 링크를 하다가 구현이 없으면 LNK에러가 발생하게 됩니다.
#pragma once
#include <functional>
#include <map>
#include <string>
#include "Actor/Stat.h"
enum class JobState { Warrior, Mage };
class Job
{
std::map<char, std::function<void()>> jobFuncMap;
public:
virtual std::string Test() { return "" } ;
virtual Stat SetBaseStat() const = 0;
virtual std::string GetJobName() const = 0;
};
그래서 위와같이 구현을 추가해주면 에러가 사라집니다.
던전 이벤트 구현을 위해 C++에서는 랜덤을 어떻게 돌리나 검색을 해봤다.
그랬더니 나는 단순하게 Rand() 함수라던가.. 를 생각했는데
무슨 메르센 트위스터 알고리즘을 이용한 난수 생성기를 이용하는게 좋다더라.....
당최 무슨 소린지 모르겠지만 일단 써보고 랜덤을 사용해야할 경우에 계속 사용하다보면 익숙해지지 않을까 싶다.............
// 하드웨어 기반 난수 생성 시드
static std::random_device rd;
// tm19937 = 난수엔진
// 메르센 트위스터 알고리즘을 사용함
// gen(seed)를 통해 생성 > gen(rd())
static std::mt19937 gen(rd());
// 균등 분포 정수 생성기(?????)
// dist() 를 통해 범위 지정
static std::uniform_real_distribution<> dist(0, 2);
dungeonEventState = static_cast<DungeonEvent>(dist(gen))
0 > Nothing
1 > EncounterEnemy
2 > FindTreasure
static은 lifetime과 scope를 고정한다.
void A()
{
static int a = 10; // 함수 끝나도 살아있음
}
최초 1회만 초기화 가능하며 함수 호출이 끝나면 값이 살아있다.
class A
{
static int count; // 해당 class로 생성된 객체와 모두 공유됨
}
A a;
A b;
a.count // 10
b.count // 10
//하지만 static 멤버변수 호출은 아래가 정석 다 공유되니까!
A::count // 10