[C++ 기초 2편] 연산자 · 클래스 설계 · main 로직 흐름

김여울·2025년 5월 13일

사전캠프

목록 보기
21/24

연산자

📎강의 자료

✅ 복합 대입 연산자

연산자의미예시설명
+=더해서 저장a += 2;a = a + 2; 와 같음
-=빼서 저장a -= 3;a = a - 3; 와 같음
*=곱해서 저장a *= 4;a = a * 4; 와 같음
/=나눠서 저장a /= 2;a = a / 2; 와 같음
%=나머지를 저장a %= 5;a = a % 5; 와 같음

✅ 증가/감소 연산자

연산자의미예시설명
++a전위 증가int b = ++a;증가 후 대입 (먼저 +1, 그다음 대입)
a++후위 증가int b = a++;대입 후 증가 (먼저 대입, 그다음 +1)
--a전위 감소--a;a에서 1 감소시킴 (바로 적용)
a--후위 감소a--;a에서 1 감소시킴 (뒤에서 적용)

🧩 예시

int a = 5;
a += 3;      // a = 8
a *= 2;      // a = 16
a--;         // a = 15
int b = a++; // b = 15, a = 16

Character 클래스

📄 Character.h

#pragma once
#include "Actor.h"  // Actor 클래스 상속

class Character : public Actor
{
public:
    Character(int hp, int ad);            // 생성자
    void Damaged(int damage) override;    // 데미지 입는 함수 (오버라이드)
    void Heal();                          // 회복 함수

private:
    int Healcount;
    int HealSlot[3] = { 3, 2, 1 };        // 회복량 저장 슬롯
};
  • public Actor Actor 클래스를 공개 상속 – 외부에서 접근 가능함
  • override 부모 클래스의 Damaged 함수 등을 **재정의(오버라이드)**할 때 사용
  • HealSlot[] 회복 가능한 값 3개를 저장하는 배열

✨ Character.cpp

#include "Character.h"
#include <iostream>

Character::Character(int hp, int ad) : Actor(hp, ad, "용사")  // 부모 생성자 호출
{
    Healcount = 0;
}

void Character::Damaged(int damage)
{
    std::cout << damage << "의 데미지를 입었다!" << std::endl;
    std::cout << "남은 체력: " << ((HP - damage > 0) ? HP - damage : 0) << std::endl;
    Actor::Damaged(damage); // 부모 클래스의 Damaged 함수 호출
}

void Character::Heal()
{
    if (Healcount < 3)
    {
        HP += HealSlot[Healcount];
        Healcount++;
        std::cout << HealSlot[Healcount - 1] << "를 회복했다! 남은 회복 횟수: "<< 3 - Healcount << std::endl;
    }
}
  • Character::Character 생성자 ➡ Actor 부모 클래스의 생성자에 값 전달
  • Actor(hp, ad, "용사") 부모 클래스 Actorhp, ad, "용사" 값을 전달
  • Healcount 회복 가능 횟수 ➡ 최대 3회로 제한
  • ? 삼항 연산자 ➡ 조건이 참이면 앞의 값, 거짓이면 뒤의 값 반환
    HP - damage > 0) ? HP - damage : 0 ➡ 체력이 0보다 작으면 0으로 대체하여 음수 방지

✅ 코드 비교

if (Healcount < 3)
{
    HP += HealSlot[Healcount];       // HP에 회복량을 더해서 저장
    // ↓ 같은 의미
    HP = HP + HealSlot[Healcount];  // HP에 회복량을 더한 값을 다시 대입
}

Enemy 클래스

📄 Enemy.h

#pragma once
#include "Actor.h"

class Enemy : public Actor
{
public:
    Enemy(int hp, int ad, std::string name, bool named);  // 생성자
    bool Named;                                           // 네임드 여부
    void Damaged(int damage) override;  // 데미지 처리 (부모 함수 재정의)
    void Move(bool front) override;     // 이동 처리 (부모 함수 재정의)
};
  • public Actor Actor 클래스를 공개 상속
  • Enemy(...) 생성자, 체력/공격력/이름/네임드 여부를 초기화
  • bool Named; 네임드 몬스터인지 여부를 저장
  • override 부모 클래스의 함수를 재정의 한다는 의미
    ➡ 실수로 함수 이름 틀리는 걸 방지함
  • Move(bool front) 방향에 따라 위치 이동을 구현할 예정

✨ Enemy.cpp

#include "Enemy.h"
#include <iostream>

// 생성자: Actor 생성자 호출 + Named 변수 초기화
Enemy::Enemy(int hp, int ad, std::string name, bool named) : Actor(hp, ad, name)
{
    Named = named;
}

// 데미지 처리 함수 (부모 함수 오버라이드)
void Enemy::Damaged(int damage)
{
    if (Named)
    {
        std::cout << "울부짖으며," << std::endl;
    }
    std::cout << "감히 나를 때렸겠다?" << std::endl;

    Actor::Damaged(damage); // 부모 함수 호출
}

// 이동 처리 함수 (앞/뒤로 이동)
void Enemy::Move(bool front)
{
    if (front)
    {
        Position--; // 전위 감소 (앞으로 이동)
    }
    else
    {
        Position++; // 후위 증가 (뒤로 이동)
    }
}
  • Enemy::Enemy(...) : Actor(...) 부모 생성자에 값 넘김 (hp, ad, name)
  • Named = named; 매개변수로 받은 값을 멤버 변수에 저장
  • if (Named) 네임드 적이면 특별한 출력 추가
  • Actor::Damaged(damage); 부모 함수 재사용
  • Position-- / Position++ 위치 이동. 앞이면 감소, 뒤면 증가

✅ 앞이면 감소(--), 뒤면 증가(++)

1차원 배열/좌표에서

[0] [1] [2] [3] [4]
 ↑   ↑   ↑   ↑   ↑
왼쪽      →      오른쪽

왼쪽으로 한 칸 ➡ index가 작아짐 (감소)
오른쪽으로 한 칸 ➡ index가 커짐 (증가)
🔻
앞(front) : 보통 왼쪽/전진/앞으로 가는 것 ➡ Position--
뒤(back) : 보통 오른쪽/후진/뒤로 가는 것 ➡ Position++

🧩 예시

int Position = 3;

if (front)
    Position--;  // 앞으로 한 칸 이동 (ex. 3 → 2)
else
    Position++;  // 뒤로 한 칸 이동 (ex. 3 → 4)

Main()


항목필요 여부이유
main.cpp✅ 반드시 필요실행 시작점
main.h❌ 거의 필요 없음다른 곳에서 main() 호출 안 함

✨ Main.cpp

✅ 주요 흐름
1. 캐릭터 생성
2. 반복문에서 적 생성 (switch 사용)
3. 위치 초기화
4. 행동 선택 (e, a, f, b, h)
5. 공격 조건 확인
6. 체력 확인 후 객체 삭제
7. 게임 종료

✅ 코드

#include "Character.h"
#include "Enemy.h"
#include <iostream>

void main()
{
    Character* character = new Character(20, 3); // 체력 20, 공격력 3

    for (int i = 0; i < 3; i++) // 총 3명의 적과 전투
    {
        Enemy* enemy = nullptr; // 아직 생성 안 됨, 후에 실제 객체 할당 예정
        switch (i)
        {
        case 0:
            enemy = new Enemy(3, 1, "잡몹", false);   // 여기서 실제 객체 생성!
            std::cout << "잡몹을 만났습니다." << std::endl;
            break;
        case 1:
            enemy = new Enemy(5, 3, "네임드몹", true);
            std::cout << "네임드몹을 만났습니다." << std::endl;
            break;
        case 2:
            enemy = new Enemy(5, 6, "보스", true);
            std::cout << "보스를 만났습니다." << std::endl;
            break;
        }

        enemy->SetPosition(5);       // 적 위치는 5
        character->SetPosition(0);   // 플레이어는 0에서 시작

        while (enemy != nullptr)
        {
            std::cout << "내 위치: " << character->GetPosition()
                      << " 적 위치: " << enemy->GetPosition() << std::endl;

            char input;
            std::cout << "행동을 선택해주세요 (a: 공격, f: 전진, b: 후진, h: 회복, e: 도망)" << std::endl;
            std::cin >> input;

            if (input == 'e') // 도망
            {
                std::cout << "도망쳤습니다" << std::endl;
                break;
            }
            else if (input == 'a') // 공격
            {
                if (enemy->GetPosition() == character->GetPosition() + 1)
                    character->Attack(enemy);
                else
                    std::cout << "공격을 했으나 닿지 않습니다." << std::endl;
            }
            else if (input == 'f') // 전진
            {
                if (enemy->GetPosition() > character->GetPosition() + 1)
                    character->Move(true);
                else
                    std::cout << "적이 가로막고있습니다" << std::endl;
            }
            else if (input == 'b') // 후진
            {
                if (0 < character->GetPosition())
                    character->Move(false);
                else
                    std::cout << "막다른 골목입니다." << std::endl;
            }
            else if (input == 'h') // 회복
            {
                character->Heal();
            }

            if (enemy->GetHP() <= 0) // 적 사망 처리
            {
                delete enemy;       // 적이 죽으면 메모리 해제
                enemy = nullptr;    // 안전하게 포인터 초기화
            }

            if (enemy == nullptr)
                break;

            // 적이 공격 범위 안에 들어왔는지 확인
            if (enemy->GetPosition() == character->GetPosition() + 1)
                enemy->Attack(character);
            else
                enemy->Move(true);

            if (character->GetHP() <= 0) // 캐릭터 사망 처리
            {
                delete character;
                character = nullptr;
            }

            if (character == nullptr)
                break;
        }

        if (character == nullptr)
            break;
    }

    std::cout << "게임 끝" << std::endl;
}
개념설명
new Class()동적 객체 생성 (힙 메모리)
->포인터로 멤버 접근
nullptr포인터가 아무 것도 가리키지 않음
delete동적 메모리 해제
switch적의 종류를 조건별로 분기
while캐릭터 vs 적 전투 루프
입력 키e: 도망, a: 공격, f: 전진, b: 후퇴, h: 힐

✅ new

Enemy* enemy = new Enemy(...);  // heap에 적 객체 생성
  • 메모리를 동적 할당
  • 메모리를 heap(힙) 영역에 생성
  • 직접 없애지 않으면 계속 메모리 차지 (→ 메모리 누수 발생 가능)

✅ delete

delete enemy;
enemy = nullptr;
  • 메모리를 동적 해제
  • 위에서 new로 만든 객체를 제거함
  • enemy는 더 이상 유효하지 않음
  • nullptr로 초기화하는 건 dangling pointer(더 이상 존재하지 않는 메모리를 가리키는 포인터) 방지용
    ➡ 이 상태에서 그 포인터로 접근하면 오류 발생 (충돌, 이상한 값, 프로그램 멈춤 등)

📌 nullptr

  • 아무 객체도 가리키지 않음
  • 포인터가 비어 있다 / 어디도 안 가리킨다
코드 / 구문의미
nullptr아무 것도 가리키지 않는 포인터
Enemy* enemy = nullptr;비어 있는 포인터 선언 (초기화)
enemy == nullptr적이 아직 생성되지 않았는지 확인
enemy != nullptr적 객체가 생성되어 유효한 상태인지 확인
enemy = new Enemy(...);실제 적 객체를 생성하고 포인터에 연결
if (enemy != nullptr)널 체크: 적이 존재할 때만 실행되는 조건
delete enemy; enemy = nullptr;적 객체 메모리 해제 후 포인터 초기화

🧠 요약

키워드의미
new객체를 heap에 동적 생성
deleteheap에 생성한 객체 메모리 해제
nullptr포인터를 비운다 (해제 후 초기화 용)

🔁 switch문 구조

  • switch문은 특정 변수의 값에 따라 실행할 코드를 분기하는 조건문
switch (조건값)
{
    case1:
        // 조건값이 값1일 때 실행할 코드
        break;

    case2:
        // 조건값이 값2일 때 실행할 코드
        break;

    default:
        // 조건값이 어떤 case에도 해당하지 않을 때 실행
        break;
}
  • case 비교할 값
  • break 해당 case 실행 후 switch문 종료 / 없으면 아래 case까지 연쇄 실행 (fall-through)
  • default 모든 case와 맞지 않을 때 실행 (선택 사항)

🔁 반복문 📎강의 자료
🔁 for문 구조

for (초기식; 조건식; 증감식)
{
    // 반복할 코드
}
for (int i = 0; i < 3; i++)
{
    // 매 반복마다 enemy를 하나 생성함
}
구성의미
int i = 0i라는 변수를 0으로 시작함
i < 3i가 3보다 작을 동안 반복
i++한 번 반복할 때마다 i를 1 증가시킴
  • i = 0부터 시작해서 i < 3일 동안 반복 → 즉, 총 3번 반복됨
  • 반복할 때마다 switch (i)를 이용해 서로 다른 적을 생성함
i 값생성되는 적설명
0잡몹일반 몬스터
1네임드몹이름 있는 중간 보스
2보스몹최종 보스

🔁 while문 구조

while (조건)
{
    // 조건이 참인 동안 계속 실행될 코드
}
  • 조건이 true일 때만 블록 안의 코드가 실행됨
  • 반복하다가 조건이 false가 되면 루프 종료
  • 적이 죽어서 delete enemy; enemy = nullptr; 되면 반복 종료

🔁 for vs while

구분for문while문
반복 횟수정해져 있는 경우 (예: 3번 반복 등)언제 끝날지 모를 조건 반복
예시for (int i = 0; i < 3; i++)while (enemy != nullptr)
쓰임새카운팅, 순차적 처리생존 여부, 네트워크 대기, 입력 반복 등

0개의 댓글