main.h
#include <iostream>
#include "Hotel.h"
#include <windows.h>
using namespace std;
int main(void)
{
Hotel hotelHandler;
while(true)
{
int select;
system("cls");
hotelHandler.DispMenu();
cout << " 선택 : [_]\b\b";
cin >> select;
switch (select)
{
case CHECKIN:
hotelHandler.CheckIn();
break;
case CHECKOUT:
hotelHandler.CheckOut();
break;
case VIEW:
hotelHandler.ViewList();
break;
case EXIT:
cout << endl<<" "; return 0;
default:
cin.clear();
cin.ignore(1000, '\n');
cout << endl << " 잘못 누르셨습니다. 다시 선택해주세요" << endl;
Sleep(500);
}
}
return 0;
}
루프 안에선 select 변수를 만들고 메뉴를 보여준뒤 사용자에게 입력을 받아서 switch case 로 각각에 메뉴로 들어간다. default 로는 문자 또는 문자열이 들어온 경우를 대비해서 입력버퍼를 비워주었다.
hotel.h
#pragma once
#include "Animal.h"
#define MAXROOM 8
typedef enum
{
CHECKIN=1,
CHECKOUT,
VIEW,
EXIT
}_Select;
class Hotel
{
private:
Animal* guest[MAXROOM]; // Animal 포인터 배열
int currentGuest; // 현재 투숙동물 수
public:
Hotel()
:currentGuest(0) // 투숙동물은 생성시 0으로 초기화
{
for(int i=0; i< MAXROOM; i++)
{
guest[i] = NULL; // NULL 포인터 대입
}
};
void DispMenu() const; // 메뉴 표시
void CheckIn(); // 체크인
void CheckOut(); // 체크아웃
void ViewList() const; // 방 조회
int ViewVacantList() const; // 빈방 보여주기
~Hotel()
{}
};
메뉴를 enum으로 선언해 두었다.
호텔 클래스에는 길이가 #define MAXROOM 인 Animal 포인터 배열과 현재 투숙동물 수를 나타내는 currentGuest가 있다.
currentGuest는 멤버이니셜라이저로 생성시 0으로 초기화하고 포인터 배열에는 다 NULL 값을 대입한다.
처음엔 방이 빈방인지 아닌지 확인하는 플래그를 두었는데 guest[i]가 NULL값을 가지고 있는지 아닌지 체크하면 빈방인지 아닌지를 알 수 있으니까 필요없어서 삭제했다.
Animal.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
typedef enum
{
DOG =1,
CAT
}_AnimalType;
class Animal
{
public:
private:
_AnimalType type;
char* name;
public:
Animal(char *myname, _AnimalType mytype)
:type(mytype)
{
name = new char[strlen(myname) + 1];strcpy(name, myname);
}
// 가상함수
virtual void SayBye() const = 0;
virtual void SayHi() const = 0;
char* GetName() const
{
return name;
}
_AnimalType GetType() const // type getter
{
return type;
}
void Setname(char* myname) // name setter
{
delete[]name;
name = new char[strlen(myname) + 1];
strcpy(name, myname);
}
char* AtypeToStr() const // 동물 타입 enum 을 Str으로 반환
{
char* d = (char*)"강아지";
char* c = (char*)"고양이";
char* e = (char*)"알수없음";
if (this->type == DOG) return d;
else if (this->type == CAT) return c;
else return e;
}
virtual ~Animal() // 가상소멸자
{
delete[]name;
}
};
class Cat : public Animal
{
public:
Cat(char* myname, _AnimalType mytype)
: Animal(myname, mytype)
{}
void SayBye() const { cout << "냥냥 이따봐"; }
void SayHi() const { cout << "냥냥 안녕"; }
};
class Dog : public Animal
{
public:
Dog(char* myname, _AnimalType mytype)
: Animal(myname, mytype)
{}
void SayBye() const { cout << "멍멍 이따봐"; }
void SayHi() const { cout << "멍멍 안녕"; }
};
Animal은 enum인 type과 chat* 형 name을 멤버변수로 갖는다.
SayBye 와 SayHi 함수는 나중에 Cat 과 Dog 가 상속할 거라서 virtual 로 선언했다. 이 외에는 Name에 대한 Getter Setter, type에 대한 Getter를 만들어 두었다.
그리고 부모 포인터를 사용해서 delete[] 할꺼기 때문에 소멸자에 virtual을 붙여 가상소멸자로 만들었다.
Cat 과 Dog는 Animal을 public으로 상속하고, 생성자를 넣고 SayBye와 SayHi 함수를 재정의 해주었다.
Hotel.cpp
#include "Hotel.h"
#include "Animal.h"
#include <iostream>
#include <windows.h>
#include <cstdio>
using namespace std;
void Hotel::DispMenu() const
{
cout << "========== 메뉴 ==========" << endl;
cout << "| |" << endl;
cout << "| 1. 체크인 |" << endl;
cout << "| |" << endl;
cout << "| 2. 체크아웃 |" << endl;
cout << "| |" << endl;
cout << "| 3. 방조회 |" << endl;
cout << "| |" << endl;
cout << "| 4. 나가기 |" << endl;
cout << "| |" << endl;
cout << "=========================" << endl;
}
int Hotel::ViewVacantList() const
{
bool choose=true;
int select = 0;
while(choose)
{
system("cls");
cout << "==================== 체크인 ====================" << endl << endl;
cout << "숙박을 원하시는 방을 선택해주세요 (현재 상태가 'X'인 방만 체크인 가능합니다)" << endl << endl;
printf("-------------------------------------------------\n");
printf("|1번방|2번방|3번방|4번방|5번방|6번방|7번방|8번방|\n");
printf("-------------------------------------------------\n");
for(int i=0; i<MAXROOM;i++)
{
if(guest[i]==NULL) printf("| %c ", 'X'); // 포인터 배열의 해당 인덱스가 NULL값이면 X
else printf("| %c ", 'O'); // 아니면 O
}
printf("|\n");
printf("-------------------------------------------------\n\n");
printf(" 선택 [_]\b\b");
cin >> select;
if(select < 1 || select > MAXROOM)
{
cin.clear(); // 잘못 입력시 입력 버퍼 비움
cin.ignore(1000, '\n');
cout << endl << "잘못 입력하셨습니다. 다시 선택해주세요..." << endl;
Sleep(500);
continue;
}else
{
if(guest[select - 1] !=NULL ) // 해당 방에 동물이 있는 경우
{
cout << endl << "해당 방에 투숙하실 수 없습니다. 다시 선택해주세요..." << endl;
Sleep(500);
}else
{
choose = false;
break;
}
}
}
return select-1; // 제대로 입력한 경우 입력값-1 리턴
}
체크인 단계에서 빈방을 보여주는 함수이다. Animal 포인터배열인 guest[i]가 NULL인지 MAXROOM까지 반복하며 NULL이면 화면에 X 표시 아니면 O 표시를 해서 사용자가 빈방을 알 수 있게끔 한다.
사용자가 범위를 넘어가는 값을 입력하거나 이미 차있는 방을 선택하는 경우에 예외처리도 했다. 제대로 선택한 경우에만 break로 빠져나와 select-1 (인덱스값)을 리턴한다.
void Hotel::CheckIn()
{
if(currentGuest < MAXROOM) // 현재 방이 꽉 찬 상태가 아니라면
{
int roomIndex = ViewVacantList(); // 사용자가 선택한 방 인덱스 반환값 저장
int select = 0;
Animal* tempPtr = 0;
cout << endl << "동물의 종류를 입력해주세요." << endl << endl;
cout << " 1. 강아지" << endl << endl;
cout << " 2. 고양이" << endl << endl;
cout << " [_]\b\b";
cin >> select;
if(select!=1 && select !=2) // 잘못입력 시 입력 버퍼 비움
{
cin.clear();
cin.ignore(1000, '\n');
cout << endl << " 잘못 입력하셨습니다. 메뉴로 돌아갑니다..." << endl;
Sleep(500);
return;
}
cout << endl << "동물의 이름을 입력해주세요 : " << endl << endl;
cout << " _______________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
char* tempName = new char[100];
cin >> tempName;
if (select == DOG) tempPtr = new Dog(tempName, DOG); // 임시포인터에 Dog 또는 Cat 동적할당
else if (select == CAT) tempPtr = new Cat(tempName, CAT);
delete[]tempName; // 동물 이름 입력받기 위해 만들었던 임시 포인터 해제
if(tempPtr!=NULL) // 동적할당에 실패하지 않았다면
{
this->guest[roomIndex] = tempPtr; // 게스트 배열의 사용자가 선택한 인덱스에 포인터 대입
cout << endl << "매니저 \"체크인이 완료되었습니다\"" << endl << endl;
cout << "투숙동물 \"";
guest[roomIndex]->SayBye();
cout << "\"" << endl << endl;
currentGuest++; // 현재 투숙객 수 증가시킴
system("pause");
}
}else
{
cout << "현재 빈 방이 없어 체크인이 불가능 합니다" << endl << endl;
system("pause");
}
}
currentGeust가 MAXROOM 보다 적은 경우에만 입력 단계로 들어간다.
void Hotel::CheckOut()
{
system("cls");
cout << "============ 체크아웃 ===========" << endl << endl;
if (currentGuest==0)
{
cout << "현재 투숙 동물이 없습니다." << endl << endl;
system("pause");
return;
}
cout << "체크아웃할 동물의 이름을 입력해주세요 : " << endl << endl;
cout << " ___________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
char* tempName = new char[100];
cin >> tempName;
int i;
int chk = 0;
for(i=0; i < MAXROOM ; i++)
{
if(guest[i]!=NULL) // 현재 동물이 있는방만 strcmp로 이름 비교
{
if (strcmp(guest[i]->GetName(), tempName) == 0)
{
chk++; // 일치하는 검색어가 있으면 chk++
break;
}
}
}
delete[]tempName; // 동물 이름 입력받기 위해 만들었던 임시 포인터 해제
if(chk==1)
{
cout << endl << "매니저 \"" << guest[i]->GetName() << " 을/를 체크아웃 합니다\"" << endl << endl;
cout << "투숙동물 \"";
guest[i]->SayHi();
cout << "\"" << endl << endl;
delete guest[i]; // 해당 포인터 동적할당 해제함
guest[i] = NULL; // NULL 포인터 대입
currentGuest--; // 현재 투숙동물 수 줄임
system("pause");
}else if(chk==0){ // 검색 결과가 없을 시
cout << endl << "일치하는 이름의 동물이 없습니다." << endl << endl;
system("pause");
}
}
현재 투숙동물이 0인 경우 바로 return 하고 char* tempName = new char[100]; 으로 사용자에게 키워드를 입력받아 MAXROOM 까지 반복하면서 현재 동물이 있는 방만 if (strcmp(guest[i]->GetName(), tempName) == 0) 으로 키워드랑 게스트 이름을 비교한다. NULL포인터를 써서 GetName()함수를 사용하려고 하면 런타임 에러가 날 것이다.
만약 일치하는 이름이 있으면 chk++하고 break로 빠져나온다. 동일한 이름의 동물이 둘 있는 경우는 고려하지 않았다. 현재 구조로는 break로 나오고 바로 i값을 써서 해제하는 방식이기 때문에 동일한 이름이 둘 있는 경우 앞쪽 인덱스가 해제될 것이다.
임시 포인터인 tempName 해제하고 guest[i] 도 해제하고 NULL을 대입해준다. currentGuest도 --한다.
처음엔 삭제한 인덱스가 마지막 인덱스거나 혹은 데이터가 0개인 경우를 제외하고 memmove 함수로 배열 인덱스를 for문 돌려 한칸씩 땡겼었는데 생각해보니 동물호텔에서 앞칸이 체크아웃했다고 뒷칸에 있는 애들을 앞으로 땡기는건 좀 이상한 것 같아서 삭제했다.
예전에 했던 도서관리 프로젝트에선 효율적으로 데이터 관리하기 위해 한 인덱스가 삭제됐을 때 한칸씩 땡기는게 맞는데 이것처럼 물리적 공간이 존재한다고 가정하면 그대로 놔두는 것이 맞다.
void Hotel::ViewList() const
{
system("cls");
printf("======== 현재 투숙동물 현황 [ %d / %d ] ========\n\n", currentGuest, MAXROOM);
printf(" 방 번호 동물 종류 동물 이름\n");
printf("----------------------------------------------\n");
for(int i=0 ; i < MAXROOM; i++) // 현재 투숙객 수만큼 반복하며 목록 프린트
{
if(guest[i]==NULL)
{
printf(" [%d] X X \n", i + 1);
}else
{
printf(" [%d] %s %s \n", i + 1, guest[i]->AtypeToStr(), guest[i]->GetName());
}
}
printf("----------------------------------------------\n\n");
cout << " ";
system("pause");
}
NULL인지 아닌지 검사해서 NULL이면 X표시 아니면 종류와 이름 표시하는데 동물 enum 타입을 char* 형태로 반환하는 AtypeTostr() 함수를 만들어놔 사용하였다.
'프로그래밍 > C++' 카테고리의 다른 글
C++ ] 2021 교통사고 통계자료 필터 프로그램 (1) + 파일 입출력 (0) | 2022.06.28 |
---|---|
C++ ] 상속을 이용한 주차타워 (0) | 2022.06.24 |
C++ ] 입력 버퍼 비우기 , 문자를 입력하면 무한루프에 빠지는 문제 해결 / cin.clear() , cin.ingnore() (0) | 2022.06.16 |
C++ ] 클래스를 활용한 자판기 프로그램 + 함수 뒤에 붙는 const의 의미 (0) | 2022.06.16 |
C++ ] 자판기 프로그램 (0) | 2022.06.16 |