
Inventory<Item> myInventory(5)로 Inventory 객체 만들때 Item기본 생성자가 없다고 하는 오류가 났다.
인벤토리의 생성자를 보면
explicit Inventory(int capacity = 10) : capacity_(capacity) {
if (capacity <= 0) {
capacity_ = 1;
}
pItems_ = new T[capacity_];
}
마지막줄에 pItems = new T[capacity]가 존재하는데 T가 Item이므로 기본생성자가 필요했다.
기존 Item의 생성자는 Item(const std::string& name, int price) : name_(name), price_(price) {} 이렇게 이름과 값을 입력받아서 초기화하는 생성자만 있었는데 기본 생성자Item(){}를 만들어서 해결하였다.
테스트 시나리오는 특히 대입연산자, 복사 생성자는 ai의 도움을 받아서 모든 케이스를 다 테스트 해보려고 했다.
std::cout << "시작" << std::endl;
std::cout << "기본 인벤토리 5칸" << std::endl;
Inventory<Item> myInventory(5);
// ================================
// 1. AddItem확인
// ================================
std::cout << "기본 아이템 획득" << std::endl;
myInventory.AddItem(Item("몽둥이", 7));
myInventory.AddItem(Item("흰티", 6));
myInventory.AddItem(Item("반바지", 5));
myInventory.PrintAllItems();
// ================================
// 2. 인벤토리 크기
// ================================
std::cout << "현재 인벤토리 크기 : " << myInventory.GetCapacity() << std::endl;
// ================================
// 3. 현재아이템 갯수
// ================================
std::cout << "현재 아이템 개수 : " << myInventory.GetSize() << std::endl;
// ================================
// 4. 인벤토리 리사이즈
// ================================
std::cout << "달팽이를 잡았다! 전리품 획득" << std::endl;
myInventory.AddItem(Item("달팽이의 껍질", 1));
std::cout << "파란달팽이를 잡았다! 전리품 획득" << std::endl;
myInventory.AddItem(Item("파란 달팽이의 껍질", 3));
myInventory.PrintAllItems();
myInventory.Resize(3);
myInventory.PrintAllItems();
// ================================
// 5. 인벤토리 확장되는지 확인
// ================================
std::cout << "빨간달팽이를 잡았다! 전리품 획득" << std::endl;
myInventory.AddItem(Item("빨간 달팽이의 껍질", 5));
myInventory.PrintAllItems();
// ================================
// 6. 마지막 원소 삭제
// ================================
myInventory.RemoveLastItem();
myInventory.PrintAllItems();
// ================================
// 7. 정렬
// ================================
myInventory.SortItems();
myInventory.PrintAllItems();
// ================================
// 8, 복사 생성자 테스트
// ================================
Inventory<Item> clone = myInventory;
std::cout << "복사 인벤토리 " << std::endl;
clone.PrintAllItems();
// 깊은 복사 테스트
myInventory.RemoveLastItem();
std::cout << "원본" << std::endl;
myInventory.PrintAllItems();
std::cout << "복사본" << std::endl;
clone.PrintAllItems();
// ================================
// 9, 대입 연산자 테스트
// ================================
Inventory<Item> other(3);
other.AddItem(Item("해킹템", 1000));
std::cout << "기본 내 인벤토리" << std::endl;
myInventory.PrintAllItems();
myInventory = other;
std::cout << "해킹 후 내 인벤토리" << std::endl;
myInventory.PrintAllItems();
// ================================
// 10. 비어있음 테스트
// ================================
myInventory.RemoveLastItem();
myInventory.PrintAllItems();
제공된 코드 분석
검색기능 추가하기
물약은 초기3개
지급 및 반환
어떤 의도로 코드를 구현했는지 정리
수정 시 기존 코드의 수정을 최소화하면서 수정할 수 있는 방안이 있는지 고려했는가
SOLID 원칙을 잘 준수하고 있는가
if (std::cin.fail()) {
std::cout << "잘못된 입력입니다. 숫자를 입력해주세요." << std::endl;
std::cin.clear();
std::cin.ignore(10000, '\n');
continue;
}
cin.ignore(10000, '\n')은 입력버퍼에 들어있을수도있는 쓰레기값을 최대 만개 혹은 줄바꿈 문자가 나올때까지 버린다.void addRecipe(const std::string& name, const std::vector<std::string>& ingredients) {
recipes.push_back(PotionRecipe(name, ingredients));
std::cout << ">> 새로운 레시피 '" << name << "'이(가) 추가되었습니다." << std::endl;
}
std::vector<PotionRecipe> recipes;에 PotionRecipe클래스의 객체를 넣게 된다.PotionRecipe searchRecipeByName(const std::string& name) {
for (PotionRecipe recipe : recipes) {
if (recipe.potionName == name) {
return recipe;
}
}
return ;
}
class PotionRecipe {
public:
std::string potionName;
std::vector<std::string> ingredients;
PotionRecipe() : potionName("") {} // 빈생성자 생성
PotionRecipe(const std::string& name, const std::vector<std::string>& ingredients)
: potionName(name), ingredients(ingredients) {
}
....
PotionRecipe searchRecipeByName(const std::string& name) {
for (PotionRecipe recipe : recipes) {
if (recipe.potionName == name) {
return recipe;
}
}
return PotionRecipe(); // 검색어와 일치하는 이름이 없으면 빈 객체 리턴
}
std::vector<PotionRecipe> searchRecipeByIngredient(const std::string& ingredient) {
std::vector<PotionRecipe> res;
for (const PotionRecipe& recipe : recipes) {
for (const std::string& ingredient_ : recipe.ingredients) {
if (ingredient == ingredient_) {
res.push_back(recipe);
break;
}
}
}
return res;
}
PotionRecipe* findRecipeByName(std::string name) {
for (const PotionRecipe& recipe : recipes) {
if (recipe.potionName == name) {
return recipe;
}
}
return PotionRecipe();
}
PotionRecipe* findRecipeByName(std::string name) {
for (PotionRecipe& recipe : recipes) {
if (recipe.potionName == name) {
return &recipe;
}
}
return nullptr;
}
이게 이해하기 어려워서 ai한테 예시를 달라고 하였다.
상황 비유: "박물관 전시품" 🖼️
PotionRecipe (객체): 아주 귀중한 그림입니다.
const PotionRecipe& (const 참조): "만지지 마시오" 팻말이 붙은 그림입니다. (눈으로만 봐야 함, 수정 불가)
PotionRecipe* (일반 포인터): 그림을 수정할 수 있는 붓과 물감을 쥔 화가 자격증입니다.
1. 기존 코드 (값 반환) 상황
"이 그림이랑 똑같은 짝퉁(복사본) 하나 그려서 가져가세요."
return recipe; // (복사 반환)
당신은 "만지지 마시오" 팻말이 붙은 그림(const)을 보고 있습니다.
그걸 보고 똑같이 베껴서 **새로운 그림(복사본)**을 그렸습니다.
이 복사본을 반환하는 겁니다.
원본 그림을 만진 게 아니니까, 원본이 const여도 아무 상관이 없죠? 그래서 에러가 안 난 겁니다.
2. 포인터 반환 코드 상황 (에러 발생)
"저 그림을 수정할 수 있는 권한(주소)을 넘겨주세요."
return &recipe; // (주소 반환)
함수 반환 타입(PotionRecipe*)은 **"그림을 마음대로 덧칠할 수 있는 권한"**을 달라는 뜻입니다.
그런데 지금 보고 있는 recipe는 **"만지지 마시오(const)"**가 붙어있습니다.
당신이 "만지지 마시오" 붙은 그림의 주소를 넘겨주면서 **"자, 여기 가서 마음대로 수정하세요"**라고 하면 어떻게 될까요?
박물관 경비원(컴파일러)이 달려와서 막습니다. "이건 수정 금지된 그림인데, 왜 수정 권한을 주려고 합니까? 안 됩니다!" 🚨
해결책은?
경비원에게 혼나지 않으려면 둘 중 하나를 해야 합니다.
1. "만지지 마시오" 팻말을 떼고 들어간다. (제가 추천한 방법)
반복문 돌릴 때 const를 뺍니다. (for (PotionRecipe& recipe : recipes))
그러면 처음부터 수정 가능한 상태로 그림을 보니까, 주소를 넘겨줘도 문제가 없습니다.
2. 반환받는 사람도 "눈으로만 볼게요"라고 약속한다.
함수 반환 타입을 const PotionRecipe*로 바꿉니다.
"수정 금지된 그림의 주소니까, 가져가서 수정은 못 하실 겁니다"라고 명시하는 거죠.
PotionRecipe* addRecipe(std::string& name, std::vector<std::string> ingredients) {
recipes.push_back(PotionRecipe(name, ingredients));
return &recipes[recipes.size()-1];
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, int> m;
m["Potion"] = 5;
cout << "A size=" << m.size() << endl;
cout << "m[Shield]=" << m["Shield"] << endl; // 없는 key 접근
cout << "B size=" << m.size() << endl;
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, int> m;
m["Bow"] = 1;
auto result = m.emplace("Bow", 999);
cout << (result.second ? "emplace success" : "emplace fail") << endl;
cout << "Bow=" << m["Bow"] << endl;
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, int> m;
m["Sword"] = 2;
// 임의 접근을 사용 하지 않고 find 를 사용
auto it = m.find("Shield");
// 출력을 예상해 봐요
if (it != m.end())
{
cout << it->second << endl;
}
else
{
cout << "not found" << endl;
}
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
void Print(const map<string, int>& m)
{
for (auto& p : m) cout << p.first << ":" << p.second << " ";
cout << endl;
}
int main()
{
map<string, int> m;
m["C"] = 3;
m["A"] = 1;
m["B"] = 2;
m["D"] = 4;
// 첫 출력 ( 원본 )
Print(m);
// 범위 요소 삭제
if (m.size() > 2)
{
auto first = m.begin();
auto last = m.end();
--last;
m.erase(next(first), last);
}
// 범위 삭제 후 출력
Print(m);
}
전역함수* 안녕하세요, 기획자 김나쁜입니다.
* 오늘도 일감을 전달드립니다.
* 아래 순서대로 UI에 캐릭터를 표시해주세요.
* 아래 요구사항의 우선순위는 위에서 아래로 높아요.
1. 성급을 기준으로 내림차순
2. 각성을 기준으로 내림차순 정렬
3. 레벨을 기준으로 내림차순 정렬
4. 예외처리: 위의 세 기준이 모두 동일할 경우, id를 기준으로 오름차순 정렬
감사합니다.
bool compareCharacters(const Character& a, const Character& b)
{
if (a.starRank != b.starRank) {
return a.starRank > b.starRank;
}
if (a.awakening != b.awakening) {
return a.awakening > b.awakening;
}
if (a.level != b.level) {
return a.level > b.level;
}
return a.id < b.id;
}