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 -> 실제 사용 테스트
// 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;
}
  • 의도된 substr문법을 활용한 풀이
#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++ 복습세션

  • STL
    • vector, map

5. 내일 계획 : 4번 과제

+ Recent posts