2026/03/13 TIL
오늘의 목표
- 아침 코드카타 풀기
- C++ 문법 1-6. 객체 지향 프로그래밍 강의 복습
- C++ 문법 1주차 숙제 / 과제
- C++ 문법 2-1. 자원 관리 강의 수강
추가 목표:
- 2-1. 자원 관리 강의 복습
챕터 1-6: 객체 지향 프로그래밍
학습 목표
- 다형성, 상속 등 객체 지향 프로그래밍의 핵심 개념을 이해합니다.
- 추상 클래스와 가상 함수를 활용하여 객체 설계를 수행합니다.
상속
Vehicle -> Bicyle / Truck Class 구현
vehicle 클래스
#include <iostream>
#include <string>
using namespace std;
class Vehicle{
protected:
string color;
int speed;
public:
Vehicle(string c, int s) : color(c), speed(s) {}
void move(){
cout << "The vehicle is moving at " << speed << " km/h." << endl;
}
void setColor(string c){
color = c;
}
string getColor(){
return color;
}
};
*문법*
1. 생성자는 멤버 초기화 리스트를 통해 멤버 변수를 초기화 할 수 있다.
2. 자식 클래스의 생성자는 부모 클래스의 생성자를 호출할 수 있다.
// 파생 클래스 1: 자전거
class Bicyle : public Vehicle{
private:
bool hasBasket;
public:
Bicycle(string c, int s, bool basket) : Vehicle(c, s), hasBasket(basket){}
void ringBell(){
cout << "Bicycle bell: Ring Ring!" << endl;
}
};
// 파생 클래스 2: 트럭
class Truck : public Vehicle {
private:
int cargoCapacity;
public:
Truck(string c, int s, int capacity) : Vehicle(c, s), cargoCapacity(capacity){}
void loadCargo(){
cout << "Truck loading cargo. Capacity: " << cargoCapacity << endl;
}
};
main 함수
int main()
{
Bicyle b("Yellow", 30, true);
Truck t("Blue", 40, 95);
b.ringBell();
t.loadCargo();
return 0;
}
<출력 결과>
Bicycle bell: Ring Ring!
Truck loading cargo. Capacity: 95 tons.
다형성
다형성이 적용되지 않은 Wolf/Lion class
#include <iostream>
#include <string>
using namespace std;
class Lion
{
public:
Lion(string word): m_word(word){}
void bark() { cout << "Lion" << " " << m_word << endl; }
private:
string m_word;
};
class Wolf
{
public:
Wolf(string word) : m_word(word) {}
void bark() { cout << "Wolf" << " " << m_word << endl; }
private:
string m_word;
};
void print(Lion lion)
{
lion.bark();
}
void print(Wolf wolf)
{
wolf.bark();
}
int main()
{
Lion lion("ahaaaaaa!");
Wolf wolf("ohhhhh");
print(lion);
print(wolf);
return 0;
}
<예측 결과>
Lion ahaaaaaa
Wolf ohhhhh
<출력 결과>
Lion ahaaaaaa!
Wolf ohhhhh
다형성이 적용되지 않은 Wolf / Lion class에 기능 추가
#include <iostream>
#include <string>
using namespace std;
class Lion
{
public:
Lion(string word):m_word(word){}
void bark() { cout << "Lion" << " " << m_word << endl; }
private:
string m_word;
};
class Wolf
{
public:
Wolf(string word) :m_word(word) {}
void bark() { cout << "Wolf" << " " << m_word << endl; }
private:
string m_word;
};
class Dog
{
public:
Dog(string word) : m_word(word) {}
void bark() { cout << "Dog" << " " << m_word << endl; }
private:
string m_word;
};
void print(Dog dog)
{
dog.bark();
}
int main()
{
Lion lion("ahaaaaaa!");
Wolf wolf("ohhhhh");
Dog dog("oooooooooooooops");
print(lion);
print(wolf);
print(dog);
return 0;
}
<출력 결과>
Lion ahaaaaaa!
Wolf ohhhhh
Dog oooooooooooooops
해당 동물들이 공통적으로 가지고 있는 변수와 함수를 뽑아 기본 클래스로 만든다.
동적 바인딩을 통해 실제 호출된 객체의 타입에 따라 적절한 파생 클래스의 함수가 실행되도록 하려면 함수 앞에 virtual 키워드를 붙여야 한다.
virtual 키워드를 붙이면 가상 함수 테이블이 생성됨.
가상 함수 테이블에는 기본 클래스에서 virtual로 선언한 함수가 파생 클래스에서 재정의된 경우 그 위치가 기록되어 동적 디스패치가 가능해진다.
*주의*
virtual을 붙이지 않을 경우 재정의된 함수가 아닌 기본 클래스의 함수가 호출된다.
다형성이 적용된 코드
#include <iostream>
#include <string>
using namespace std;
class Animal
{
public:
Animal(){}
virtual void bark() {};
};
class Lion : public Animal
{
public:
Lion(string word) : m_word(word) {}
void bark() { cout << "Lion" << " " << m_word << endl; }
private:
string m_word;
};
class Wolf : public Animal
{
public:
Wolf(string word) : m_word(word) {}
void bark() { cout << "Wolf" << " " << m_word << endl; };
private:
string m_word;
};
class Dog : public Animal
{
public:
Dog(string word) : m_word(word) {}
void bark() { cout << "Wolf" << m_word << endl; }
private:
string m_word;
};
void print(Animal* animal)
{
animal->bark();
}
int main()
{
Lion lion("ahaaaaaa!");
Wolf wolf("ohhhhh");
Dog dog("oooooooooooooops")
print(&lion);
print(&wolf);
print(&dog);
return 0;
}
다형성 적용 결과
void print(Animal* animal) 함수에서 Animal 포인터 인자로 Lion/Wolf/Dog 변수(인스턴스)를 입력받음
해당 포인터 인자로 기본 클래스의 virtual 함수인 bark()를 실행함으로서 각각의 class의 재정의된 bark()를 불러옴.
-> 여러 번 같은 구조의 print() 함수를 정의할 필요가 없어 효율적!
<예측 결과>
Lion ahaaaaaa!
Wolf ohhhhh
Dog oooooooooooooops
<출력 결과>
Lion ahaaaaaa!
Wolf ohhhhh
Dog oooooooooooooops
다형성 관련 문법 좀 더 알아보기
가상함수 예시코드
#include <iostream>
using namespace std;
// 기본 클래스: Animal
class Animal
{
// 가상 함수: 자식 클래스에서 재정의 가능
virtual void makeSound(){
cout << "Animal makes a sound." << endl;
}
};
// 파생 클래스: Dog
class Dog : public Animal {
public:
void makeSound(){
cout << "Dog barks: Woof! Woof!" << endl;
}
};
// 파생 클래스: Cat
class Cat : public Animal{
public:
void makeSound(){
cout << "Cat meows: Meow! Meow!" << endl;
}
};
int main(){
// Animal 타입 포인터로 다양한 객체를 가리킴
Animal* myAnimal;
Dog myDog;
Cat myCat;
// Dog 개체 가리키기
myAnimal = &myDog;
myAnimal->makeSound(); // Dog의 makeSound() 호출
// Cat 개체 가리키기
myAnimal = &myCat;
myAnimal->makeSound(); // Cat의 makeSound() 호출
return 0
}
<예측 결과>
Dog barks: Woof! Woof!
Cat meows: Meow! Meow!
<출력 결과>
Dog barks: Woof! Woof!
Cat meows: Meow! Meow!
순수가상함수 예시코드
#include <iostream>
using namespace std;
// 기본 클래스: Animal
class Animmal{
public:
// 가상 함수: 자식 클래스에서 재정의 가능
virtual void makeSound() = 0;
};
// 파생 클래스: Dog
class Dog : public Animal{
public:
void makeSound(){
cout << "Dog barks: Woof! Woof!" << endl;
}
};
// 파생 클래스: Cat
class Cat : public Animal{
public:
void makeSound(){
cout << "cat meows: Meow! Meow!" << endl;
}
};
int main(){
// Animal 타입 포인터로 다양한 객체를 가리킴
Animal* myAnimal;
Dog myDog;
Cat myCat;
// Dog 객체 가리키기
myAnimal = &myDog;
myAnimal->makeSound(); // Dog의 makeSound() 호출
// Cat 객체 가리키기
myAnimal = &myCat;
myAnimal -> makeSound(); // Cat의 makeSound() 호출
return 0;
}
<예측 결과>
Dog barks: Woof! Woof!
Cat meows: Meow! Meow!
<출력 결과>
Dog barks: Woof! Woof!
Cat meows: Meow! Meow!
숙제
다형성을 활용한 게임 스킬 사용 프로그램
다형성을 이용해 다양한 직업을 가진 모험가들이 각기 다른 스킬을 사용하는 프로그램을 구현합니다.
<구상>
기본 클래스와 파생 클래스 정의이므로 주어진 대로 바로 구현하면 될 것 같다.
#include <iostream>
using namespace std;
class Adventure {
public:
virtual void useSkill() = 0;
};
class Warrior : public Adventure {
public:
void useSkill() {
cout << "Warrior uses Slash!" << endl;
}
};
class Mage : public Adventure {
public:
void useSkill() {
cout << "Mage casts Fireball!" << endl;
}
};
class Archer : public Adventure {
void useSkill() {
cout << "Archer shoots an Arrow!" << endl;
}
};
int main() {
Adventure* myAdventure;
Warrior myWarrior;
Mage myMage;
Archer myArcher;
myAdventure = &myWarrior;
myAdventure->useSkill();
myAdventure = &myMage;
myAdventure->useSkill();
myAdventure = &myArcher;
myAdventure->useSkill();
return 0;
}
<출력 결과>
Warrior uses Slash!
Mage casts Fireball!
Archer shoots an Arrow!
완성!
<답안 확인>
답안과 다른 점을 체크해본다.
int main() {
// 다형성 사용
vector<Adventurer*> adventurers;
adventurers.push_back(new Warrior());
adventurers.push_back(new Mage());
adventurers.push_back(new Archer());
// 각 모험가의 스킬 사용
for (size_t i = 0; i < adventurers.size(); ++i) {
adventurers[i]->useSkill();
}
// 메모리 해제
for (size_t i = 0; i < adventurers.size(); ++i) {
delete adventurers[i];
}
return 0;
}
main함수에서 adventurers의 포인터 함수를 만들어서 미리 변수 주소값을 저장해둔 뒤 반복문을 통해 함수를 호출하는 방식으로 효율적으로 작성되어 있다.
이 부분만 다음에 유념할 것!
C++문법 1주차 과제
<고민>
#include <iostream>
using namespace std;
int HP, MP, AP, DP;
void setPotion(int count, int* p_HPPotion, int* p_MPPotion) {
switch (count) {
case 1:
if (*p_MPPotion != 0) {
*p_MPPotion -= 1;
MP += 20;
}
cout << "* MP가 20 증가되었습니다. 포션이 1개 차감됩니다." << endl;
cout << "남은 포션 수: " << *p_MPPotion;
break;
case 2:
if (*p_HPPotion != 0) {
*p_HPPotion -= 1;
HP += 20;
}
cout << "* HP가 20 증가되었습니다. 포션이 1개 차감됩니다." << endl;
cout << "남은 포션 수: " << *p_HPPotion;
break;
case 3: // 공격력 2배
AP *= 2;
cout << "* 공격력이 2배로 증가되었습니다." << endl;
break;
case 4: // 방어력 2배
DP *= 2;
cout << "* 방어력이 2배로 증가되었습니다." << endl;
break;
case 5: // 능력치 출력
cout << "HP: " << HP << endl;
cout << "MP: " << MP << endl;
cout << "공격력: " << AP << endl;
cout << "방어력: " << DP << endl;
break;
case 6:
*p_HPPotion += 1;
*p_MPPotion += 1;
cout << "* 레벨업! HP/MP 포션이 지급됩니다." << endl;
cout << "남은 HP/MP 포션 수 : " << HP << "/" << MP << endl;
break;
case 0:
cout << "프로그램을 종료합니다." << endl;
return;
break;
default:
break;
}
}
int main() {
int status[4] = { 0 };
int HPPotion, MPPotion;
cout << "HP와 MP를 차례대로 입력하세요: ";
cin >> HP >> MP;
// 입력값이 50 이하일 시 다시 입력받기
while (1) {
if (HP <= 50 || MP <= 50) {
cout << "50 이하는 입력할 수 없습니다. 다시 입력해주세요: ";
cin >> HP >> MP;
}
else {
break;
}
}
status[0] = HP;
status[1] = MP;
cout << "공격력과 방어력을 차례대로 입력하세요: ";
cin >> AP >> DP;
//입력값이 0 이하일 시 다시 입력받기
while (1) {
if (AP <= 0 || DP <= 0) {
cout << "0 이하는 입력할 수 없습니다. 다시 입력해주세요: ";
cin >> AP >> DP;
}
else {
break;
}
}
status[2] = AP;
status[3] = DP;
HPPotion = 5, MPPotion = 5;
cout << "* 포션이 지급되었습니다. (HP, MP 포션 각 5개)" << endl;
cout << "=============================================" << endl;
// 메뉴판 무한루프
int count;
while (1) {
cout << "<스탯 관리 시스템>" << endl;
cout << "1.HP UP" << endl;
cout << "2.MP UP" << endl;
cout << "3.공격력 UP" << endl;
cout << "4.방어력 UP" << endl;
cout << "5.현재 능력치" << endl;
cout << "6.Level Up" << endl;
cout << "0.나가기" << endl;
cout << "번호 입력: ";
cin >> count;
// 포션 시스템
setPotion(count, &HPPotion, &MPPotion);
return 0;
}
}
이렇게 코드를 작성했는데, 한 번 메뉴를 선택하고 실행하고 나면 루프가 끊겨버린다.
이유를 찾아보자...
이제보니 return 0이 while loop 안에 들어있다.
빼내주고 실행해보겠다.
번호 입력: 0
프로그램을 종료합니다.
<스탯 관리 시스템>
1.HP UP
2.MP UP
3.공격력 UP
4.방어력 UP
5.현재 능력치
6.Level Up
0.나가기
번호 입력: 0
프로그램을 종료합니다.
<스탯 관리 시스템>
1.HP UP
2.MP UP
3.공격력 UP
4.방어력 UP
5.현재 능력치
6.Level Up
0.나가기
번호 입력:
프로그램 종료가 제대로 되지 않는 오류가 있다.
case 0:
cout << "프로그램을 종료합니다." << endl;
return;
break;
프로그램 종료 로직을 return;으로 구현했다고 생각했는데 함수 내에서만 사용되는 모양이다.
count 0를 입력하면 바깥에서 return 0;를 바로 출력하도록 만들어주자.
#include <iostream>
using namespace std;
int HP, MP, AP, DP, LV;
void setPotion(int count, int* p_HPPotion, int* p_MPPotion) {
switch (count) {
case 1:
if (*p_MPPotion != 0) {
*p_MPPotion -= 1;
MP += 20;
cout << "* MP가 20 증가되었습니다. 포션이 1개 차감됩니다." << endl;
cout << "남은 포션 수: " << *p_MPPotion << endl;
}
else {
cout << "포션이 부족합니다." << endl;
}
break;
case 2:
if (*p_HPPotion != 0) {
*p_HPPotion -= 1;
HP += 20;
cout << "* HP가 20 증가되었습니다. 포션이 1개 차감됩니다." << endl;
cout << "남은 포션 수: " << *p_HPPotion << endl;
}
else {
cout << "포션이 부족합니다." << endl;
}
break;
case 3: // 공격력 2배
AP *= 2;
cout << "* 공격력이 2배로 증가되었습니다." << endl;
break;
case 4: // 방어력 2배
DP *= 2;
cout << "* 방어력이 2배로 증가되었습니다." << endl;
break;
case 5: // 능력치 출력
cout << "HP: " << HP << endl;
cout << "MP: " << MP << endl;
cout << "공격력: " << AP << endl;
cout << "방어력: " << DP << endl;
break;
case 6:
*p_HPPotion += 1;
*p_MPPotion += 1;
cout << "* 레벨업! HP/MP 포션이 지급됩니다." << endl;
cout << "남은 HP/MP 포션 수 : " << *p_HPPotion << "/" << *p_MPPotion << endl;
break;
default:
break;
}
}
int main() {
int status[4] = { 0 };
int HPPotion, MPPotion;
cout << "HP와 MP를 차례대로 입력하세요: ";
cin >> HP >> MP;
// 입력값이 50 이하일 시 다시 입력받기
while (1) {
if (HP <= 50 || MP <= 50) {
cout << "50 이하는 입력할 수 없습니다. 다시 입력해주세요: ";
cin >> HP >> MP;
}
else {
break;
}
}
status[0] = HP;
status[1] = MP;
cout << "공격력과 방어력을 차례대로 입력하세요: ";
cin >> AP >> DP;
//입력값이 0 이하일 시 다시 입력받기
while (1) {
if (AP <= 0 || DP <= 0) {
cout << "0 이하는 입력할 수 없습니다. 다시 입력해주세요: ";
cin >> AP >> DP;
}
else {
break;
}
}
status[2] = AP;
status[3] = DP;
HPPotion = 5, MPPotion = 5;
cout << "* 포션이 지급되었습니다. (HP, MP 포션 각 5개)" << endl;
cout << "=============================================" << endl;
// 메뉴판 무한루프
int count;
while (1) {
cout << "<스탯 관리 시스템>" << endl;
cout << "1.HP UP" << endl;
cout << "2.MP UP" << endl;
cout << "3.공격력 UP" << endl;
cout << "4.방어력 UP" << endl;
cout << "5.현재 능력치" << endl;
cout << "6.Level Up" << endl;
cout << "0.나가기" << endl;
cout << "번호 입력: ";
cin >> count;
if (count == 0) {
cout << "프로그램을 종료합니다." << endl;
break;
}
// 포션 시스템
setPotion(count, &HPPotion, &MPPotion);
}
return 0;
}
수정한 버전이다! 잘 작동한다.
오늘의 배운 점: 다형성을 적용할 때 기본 클래스의 함수를 파생 클래스에서 재정의 한 경우 기본 클래스를 포인터 변수로 선언해서 파생 클래스를 저장하고 포인터로 함수를 가져오는 방식으로 재정의한 함수를 저장하고 사용할 수 있다.
오늘의 아쉬운 점: 과제를 할 때 자꾸 return 0;의 위치 등 사소한 부분을 틀려서 시간을 많이 잡아먹는다. 위치 하나하나 신경쓰면서 코딩할 것.