1. Day16
- C++ 문법 학습
- 알고리즘 특강
- CH2 학습 가이드 - 12/18
- C++ 복습 세션
2. C++ 문법 학습(Day16)
- 과제 3 : 인벤토리 시스템 구현
- Inventory.h -> 템플릿 헤더 정의 포함
// Inventory.h -> 템플릿 헤더 정의 포함
#pragma once
#include <iostream>
#include <algorithm>
#include "ItemBase.h"
using namespace std;
// 포인터용 비교 함수: pItems_가 포인터 배열이므로 매개변수도 포인터로 받음
bool compareItemsByPrice(ItemBase* a, ItemBase* b) {
return a->GetPrice() < b->GetPrice();
}
template <typename T>
class Inventory
{
private:
T* pItems_ = nullptr;
int capacity_;
int size_;
public:
// 생성자 소멸자
Inventory(int capacity = 10);
//복사 생성자
Inventory(const Inventory<T>& other);
~Inventory();
// 외부에서 인벤토리 객체를 조작하는 기능
void AddItem(const T& item);
void RemoveLastItem();
void Resize(int newCapacity);
void SortItems();
// 대입 함수(이미 존재하는 객체에 값을 덮어쓸 때
void Assign(const Inventory<T>& other);
// Getter
int GetSize() const { return size_; }
int GetCapacity() const { return capacity_; }
// 아이템 정보 출력
void PrintAllItems() const;
};
template<typename T>
inline Inventory<T>::Inventory(int capacity) : capacity_(capacity), size_(0)
{
// 0 이하의 입력이 들어오면 기본 용량 1 할당
if (capacity <= 0)
{
capacity_ = 1;
}
// 받은 용량만큼 배열 동적 할당
pItems_ = new T[capacity_];
}
template<typename T>
inline Inventory<T>::Inventory(const Inventory<T>& other)
{
// 복사생성자로 다른 객체의 값들을 복사하기위해 capacity, size값 복사 new T[capacity]로 동적 할당
capacity_ = other.capacity_;
size_ = other.size_;
pItems_ = new T[capacity_];
//other의 pItems_의 값들을 복사(깊은복사)
for (int i = 0; i < size_; ++i)
{
pItems_[i] = other.pItems_[i];
}
cout << "인벤토리 복사 완료" << '\n';
}
template<typename T>
inline Inventory<T>::~Inventory()
{
// 소멸자에서 배열 해제 및 nullptr 입력(nullptr이 아니라면)
if (pItems_ != nullptr)
{
delete[] pItems_;
pItems_ = nullptr;
}
cout << "소멸자 호출" << '\n';
}
template<typename T>
inline void Inventory<T>::AddItem(const T& item)
{
// 인벤토리 공간 체크
if (size_ >= capacity_)
{
cout << "인벤토리가 꽉 차서 확장합니다." << '\n';
// 확장하기 위해 새 공간을 기존 공간 * 2로 설정 그 크기만큼 동적할당한 임시포인터 생성
int newCapacity = capacity_ * 2;
T* temp = new T[newCapacity];
// 임시 포인터에 기존의 값들을 복사
for (int i = 0; i < size_; i++)
{
temp[i] = pItems_[i];
}
// 기존 포인터 해제
delete[] pItems_;
// 기존 포인터에 임시 포인터 주소 대입, capacity_도 새 크기로 갱신
pItems_ = temp;
capacity_ = newCapacity;
}
// 추가하려던 새 item을 추가 후 size++
pItems_[size_] = item;
size_++;
}
template<typename T>
inline void Inventory<T>::RemoveLastItem()
{
// 인벤토리 공간 체크
if (size_ == 0)
{
cout << "인벤토리가 비어있습니다." << '\n';
return;
}
// 배열의 마지막 item 삭제, size--
pItems_[size_-1] = T();
size_--;
}
template<typename T>
inline void Inventory<T>::Resize(int newCapacity)
{
// 새로 설정하는 공간이 0보다 작다면 생성자 규칙과 같게 1로 설정
if (newCapacity <= 0)
{
newCapacity = 1;
}
// 재설정된 capacity 크기만큼 동적 할당
T* temp = new T[newCapacity];
// 새로 설정된 용량과 기존 size_중 작은 값만큼 copySize로 설정한 다음 아이템 데이터 복사(깊은 복사)
int copySize = min(newCapacity, size_);
for (int i = 0; i < copySize; i++)
{
temp[i] = pItems_[i];
}
// 기존 포인터 해제 및 임시포인터의 주소를 가리키도록 설정 capcity_, size_ 값 재설정
delete[] pItems_;
pItems_ = temp;
capacity_ = newCapacity;
size_ = copySize;
}
template<typename T>
inline void Inventory<T>::SortItems()
{
// 아이템이 1개 이하면 정렬 필요 X
if (size_ <= 1)
{
return;
}
// sort(시작, 끝): 끝은 마지막 요소 다음을 가리킴
// pItems_ + size_ = 배열 끝 다음 위치
sort(pItems_, pItems_ + size_, compareItemsByPrice);
}
template<typename T>
inline void Inventory<T>::Assign(const Inventory<T>& other)
{
// 자기 자신에게 대입하는 경우 무시
if (this == &other)
{
return;
}
// 기존 포인터 해제
delete[] pItems_;
// capacity_, size_ 재설정 포인터 새로 동적할당
capacity_ = other.capacity_;
size_ = other.size_;
pItems_ = new T[capacity_];
// 아이템 데이터 깊은복사
for (int i = 0; i < size_; ++i) {
pItems_[i] = other.pItems_[i];
}
cout << "인벤토리 복사 완료" << endl;
}
template<typename T>
inline void Inventory<T>::PrintAllItems() const
{
if (size_ == 0)
{
cout << "(비어있음)" << '\n';
return;
}
for (int i = 0; i < size_; i++)
{
// pItems_가 담고있는 클래스 객체의 PrintInfo()함수 호출
pItems_[i]->PrintInfo();
}
}
- ItemBase.h/cpp 다른 자식 아이템(Weapon, Potion) 클래스는 사실상 중복이라 제외
// ItemBase.h/cpp 다른 자식 아이템(Weapon, Potion) 클래스는 사실상 중복이라 제외
#pragma once
#include <string>
class ItemBase
{
protected:
std::string name_;
int price_;
public:
// 생성과 동시에 아이템 이름, 가격 할당
ItemBase(std::string name, int price) : name_(name), price_(price) {}
// 아이템 정보 출력
void PrintInfo() const;
// 게터 함수(이름, 가격)
std::string GetName() const { return name_; }
int GetPrice() const { return price_; }
// 기본 소멸자 상속 대비 virtual사용
virtual ~ItemBase() = default;
};
// main.cpp -> 실제 사용 테스트
#include <iostream>
#include "Inventory.h"
#include "ItemBase.h"
#include "Weapon.h"
#include "Potion.h"
#include <string>
using namespace std;
int main()
{
// 아이템 생성
Weapon sword = { "Sword", 500 };
Weapon axe = { "Axe", 300 };
Potion hp = { "HpPotion", 100 };
Potion mp = { "MpPotion", 200 };
// 인벤토리 생성
Inventory<ItemBase*> inven(5);
// 아이템 추가
cout << "=== 아이템 추가 ===" << endl;
inven.AddItem(&sword);
inven.AddItem(&axe);
inven.AddItem(&hp);
inven.AddItem(&mp);
inven.PrintAllItems();
cout << "Size: " << inven.GetSize() << ", Capacity: " << inven.GetCapacity() << endl;
// 정렬 테스트
cout << "\n=== 가격 오름차순 정렬 ===" << endl;
inven.SortItems();
inven.PrintAllItems();
// 삭제 테스트
cout << "\n=== 마지막 아이템 삭제 ===" << endl;
inven.RemoveLastItem();
inven.PrintAllItems();
// 복사 생성자 테스트
cout << "\n=== 복사 생성자 테스트 ===" << endl;
{
Inventory<ItemBase*> inven2(inven);
cout << "inven2 Size: " << inven2.GetSize() << endl;
inven2.PrintAllItems();
}
cout << "블록 종료 후 원본 확인:" << endl;
inven.PrintAllItems();
// Resize 테스트
cout << "\n=== Resize 테스트 (용량 2로 축소) ===" << endl;
inven.Resize(2);
cout << "Size: " << inven.GetSize() << ", Capacity: " << inven.GetCapacity() << endl;
inven.PrintAllItems();
// 용량 초과 자동 확장 테스트
cout << "\n=== 자동 확장 테스트 ===" << endl;
inven.AddItem(&sword);
inven.AddItem(&axe);
inven.AddItem(&hp);
cout << "Size: " << inven.GetSize() << ", Capacity: " << inven.GetCapacity() << endl;
// Assign 테스트
cout << "\n=== Assign 테스트 ===" << endl;
Inventory<ItemBase*> inven3(3);
inven3.AddItem(&mp);
cout << "대입 전 inven3:" << endl;
inven3.PrintAllItems();
inven3.Assign(inven);
cout << "대입 후 inven3:" << endl;
inven3.PrintAllItems();
cout << "\n=== 프로그램 종료 ===" << endl;
return 0;
}
3. 알고리즘 특강
- 예제 첫 시도에 설명을 다 읽지 않고 떠오르는 방식대로 queue를 이용해서 진행
#include <iostream>
#include <string>
#include <queue>
using namespace std;
int main() {
queue<char> q;
string str;
cin >> str;
for (int i = 0; i < str.size(); i++)
{
q.push(str[i]);
}
for (int i = 0; i < q.size(); i++)
{
char temp = q.front();
q.push(temp);
queue<char> qTemp = q;
q.pop();
for (int j = 0; j < q.size(); j++)
{
cout << qTemp.front();
qTemp.pop();
}
cout << '\n';
}
return 0;
}
#include <iostream>
#include <string>
#include <vector>
// using namespace std;를 생략하면 namespace를 붙여야 합니다!
// 아래처럼 vector -> std::vector로 vector가 어느 namespace에 있는지 지정하는거에요!
// const 참조자 형태로 매개변수를 넘기는 이유는
// 1. word 수정 방지 + 2. 불필요한 복사를 방지 하기 위함인데 저번 시간에 배우셨죠!
std::vector<std::string> rotateWord(const std::string& word) {
std::vector<std::string> rotations;
for (size_t i = 0; i < word.size(); i++) {
std::string rotated = word.substr(i) + word.substr(0, i);
rotations.push_back(rotated);
}
return rotations;
}
int main() {
std::string input = "abc";
auto result = rotateWord(input);
std::cout << "[";
for (size_t i = 0; i < result.size(); i++) {
std::cout << "\"" << result[i] << "\"";
if (i < result.size() - 1) {
std::cout << ", ";
}
}
std::cout << "]\n";
}
3. CH2 학습 가이드 - 12/18
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // std::sort 포함
using namespace std;
// 캐릭터 데이터 구조체
struct Character
{
int id; // 고유 식별자 (ID)
int starRank; // 성급
int awakening; // 각성 단계
int level; // 레벨
void print() const
{
cout << "ID: " << id << " 성급: " << starRank;
cout << " 각성: " << awakening << " Lv: " << level << endl;
}
};
bool compareCharacters(const Character& a, const Character& b)
{
// 성급이 같다면
if (a.starRank == b.starRank)
{
// 각성이 같다면
if (a.awakening == b.awakening)
{
//레벨이 같다면
if (a.level == b.level)
{
// Id 오름차순 반환
return a.id < b.id;
}
//레벨 내림차순 반환
return a.level > b.level;
}
//각성 내림차순 반환
return a.awakening > b.awakening;
}
// 성급이 서로 같지 않다면 성급 내림차순 반환
return a.starRank > b.starRank;
}
int main()
{
vector<Character> characters =
{
{101, 5, 3, 50},
{102, 5, 3, 70},
{103, 5, 3, 70},
{104, 4, 5, 99}
};
cout << "--- 정렬 전 ---" << endl;
for (const auto& c : characters)
{
c.print();
}
sort(characters.begin(), characters.end(), compareCharacters);
cout << "--- 정렬 후 ---" << endl;
// 예상 결과: ID 102, 103, 101, 104 순으로 출력되어야 합니다.
for (const auto& c : characters)
{
c.print();
}
return 0;
}
4. C++ 복습세션
5. 내일 계획 : 4번 과제