캠프 14일차
26. 7. 15.약 1 분
캠프 14일차
Atani 퀴즈
프로그래머스 코테 연습
C++
코드 연습
던전 탈출 텍스트 RPG
- 전역
using namespace std;제거- 모든 헤더/소스에서
using namespace std;를 지우고std::string,std::cout처럼 명시적으로 적도록 정리. 헤더에using namespace std;가 있으면 그 헤더를 include하는 모든 파일에 전역으로 퍼지기 때문에, 파일 수가 늘어난 지금 시점에 미리 정리해두었다.
- 모든 헤더/소스에서
Item/Potion다형성 실제 연결- 어제 미완성으로 남겨뒀던 부분.
Item이PlayerInventory&,PlayerStatus&를 참조로 들고 있도록 하고,Potion이 이를 상속받아use(itemName, count)에서 인벤토리 소모 + 스탯 회복을 함께 처리하도록 구현 PlayerEnhancement에서canUsePotion/usePotion/getPotionEffect로 나뉘어 있던 로직을 지우고potion.use(...)호출 하나로 대체
- 어제 미완성으로 남겨뒀던 부분.
- 아이템 종류 확장을 위한 자료구조 정리
InventoryItem에itemCategory(포션/재료 구분)와optional<StatusType> status(회복시킬 스탯, 재료류는 없으므로nullopt)를 추가Item/Potion/PotionInfo.h: HP/MP 소형·중형 포션, AP/DP 소형 포션을 상수로 정의Item/Material/MaterialInfo.h: 몬스터 드랍템(끈적한 젤리/고블린 가죽/늑대 가죽)을DropItemType→InventoryItem맵(dropItemMap)으로 정의해,Monster::getDropItem()이pair<string, int>대신InventoryItem을 바로 반환하도록 변경
PlayerInventory리팩터링addInventory→addItem으로 이름 정리,getItem(name)이optional<InventoryItem>을 반환하도록 해서 존재 여부 확인과 값 조회를 한 번에 처리removeItem(name, count)추가. 성공/수량 부족/아이템 없음을ErrorCodes.h에 새로 정의한RemoveItemResultenum으로 구분해서 반환- 인벤토리 최대 용량을 매직넘버
10으로 박아두던 것을maxCapacity멤버(현재 20)로 분리
// Item.h / Potion.h — Item을 상속받아 실제 아이템 사용 로직 구현
class Item {
protected:
PlayerInventory& inventory;
PlayerStatus& status;
public:
Item(PlayerInventory& inventory, PlayerStatus& status);
virtual ~Item() = default;
};
class Potion : public Item {
public:
Potion(PlayerInventory& inventory, PlayerStatus& status);
void use(const std::string& itemName, const int& count);
};// Potion.cpp — RemoveItemResult로 분기해서 소모 + 회복을 함께 처리
void Potion::use(const std::string& itemName, const int& count) {
std::cout << "포션을 사용합니다.\n";
RemoveItemResult result = inventory.removeItem(itemName, count);
switch (result) {
case RemoveItemResult::Success : {
auto item = inventory.getItem(itemName);
if(item->status.has_value()) {
StatModifier p{std::plus<int>(), (item->effect) * count};
status.controlPlayerStatus(item->status.value(), p);
}
break;
}
case RemoveItemResult::InsufficientCount :
std::cout << "입력한 값이 보유 수량보다 큽니다.\n";
break;
case RemoveItemResult::NotFound :
std::cout << "해당 아이템은 인벤토리에 없습니다.\n";
break;
default:
break;
}
}- 어제 걱정했던 "인벤토리가
map<string, InventoryItem>기반 데이터로만 동작하는데Item::use()다형성과 어떻게 연결할지" 문제는,Item이 인벤토리/스탯 참조를 직접 들고 있게 하는 방식으로 풀었다.Potion입장에서는 그냥 자기 재료(포션 이름)로 인벤토리에서 빼고, 빠지는 데 성공하면 자기 효과만큼 스탯을 올리면 되니 생각보다 깔끔하게 연결됐다. - 다만
Material은 아직 빈 클래스로만 만들어둔 상태라, 재료 아이템에 실제로 무슨 동작을 붙일지는 다음에 더 생각해봐야 할 듯
std::optional
값이 "있을 수도, 없을 수도" 있는 상황을 표현하는 타입으로, PlayerInventory::getItem()처럼 아이템이 인벤토리에 있는지 없는지 모르는 상태에서 조회할 때 사용했다.
#include <optional>
std::optional<InventoryItem> PlayerInventory::getItem(const std::string& itemName) const {
auto it = inventory.find(itemName);
if(it != inventory.end()) {
return it->second;
} else {
return std::nullopt;
}
}- 반환값을 그냥
bool(존재 여부)로 주거나 포인터로 줄 수도 있지만,optional을 쓰면 "값이 있으면 그 값 자체, 없으면nullopt"를 하나의 타입으로 표현할 수 있어서 호출부에서if(item)으로 존재 확인과item->effect로 값 접근을 자연스럽게 이어갈 수 있다. InventoryItem의status필드도std::optional<StatusType>으로 선언해서, 포션류는 회복시킬 스탯을 담고 재료류는nullopt로 비워둘 수 있게 했다. 재료를 위해StatusType에 별도의None값을 추가하지 않아도 되는 점이 마음에 들었다.
정리
optional<T>는 "T 타입 값이 있을 수도, 없을 수도 있음"을 표현하며,bool/포인터로 존재 여부만 따로 다루는 것보다 값과 존재 여부를 한 번에 다룰 수 있다.has_value()(또는if(opt))로 존재 여부를 확인하고,value()나->/*로 내부 값에 접근한다.- 값이 없을 때는
std::nullopt를 반환한다.
