[42Seoul] CPP Module 00 - ex01

수빈·2022년 1월 1일
1

42CPP

목록 보기
3/11
post-thumbnail

Exercise01

  • Exercise01 : My Awesome PhoneBook 다음과 같은 조건을 만족하는 전화번호부 프로그램을 작성하라.
    • 동적 할당을 하지 않고 최대 8개의 연락처를 저장할 수 있다.

    • 프로그램의 명령은 EXIT, ADD, SEARCH만 인식하며, 그 외의 경우는 모두 무시한다.

    • EXIT프로그램이 종료되며 모든 연락처가 삭제된다.

    • ADD연락처의 모든 정보가 채워질 때 까지 표준입력을 통해 정보를 입력받는다.

      • 연락처의 정보는 : first name, last name, nickname, phone number, darkest secret으로 구성되어 있다.
      • 전화번호부 클래스는 연락처 배열을 포함해야 한다.
      • 연락처는 반드시 코드에서 클래스의 인스턴스로 구현되어야 한다.
      • 만약 전화번호부에 8개의 연락처가 모두 차 있는 경우, 가장 마지막에 있는 연락처를 지우고 새로운 연락처를 해당 인덱스에 넣어야 한다.
    • SEARCH전화번호부 프로그램은 비어있지 않은 연락처의 4개 열을 출력한다 : index, first name, last name, nickname
      - 각 열의 너비는 10글자이며, 우측 정렬이고, 각 열은 | 기호로 분리된다. 또한 10글자를 초과한 항목의 경우 항목 + .을 10글자 길이로 맞추어 출력한다.
      - index를 추가로 입력할 시 해당 연락처의 모든 정보를 출력한다. 입력이 이상할 경우 특정 동작을 정의해야 한다.

      해당 과제에서는 클래스라는 새로운 개념을 학습해야 한다.

Class

구조체 선언과 마찬가지로 클래스 선언은 메모리에 할당되지 않는다.
클래스가 어떻게 보이는지만 정의한다.
클래스를 사용하려면 해당 클래스 타입의 변수를 선언해야 한다.

데이터를 보유하는 것 외에도 클래스에는 함수가 포함될 수 있다.
클래스 내부에 선언된 함수를 멤버 함수라고 한다 == 메소드라고 불리기도 한다.
멤버 함수는 클래스 정의의 내부 또는 외부에서 정의할 수 있다. 해당 예제는 내부에 정의.

struct DateStruct 
{ 
	int year; 
	int month;
	int day; 
};

class DateClass 
{ 
	public:
		int m_year; 
		int m_month;
		int m_day; 

	void print()
	{ 
		std::cout << m_year << "/" << m_month << "/" << m_day; 
	}
};
int main() 
{ 
	DateClass birthDay { 2019, 7, 28 }; 

	birthDay.m_year = 1992; // 클래스의 멤버 변수 접근, 값 수정 
	birthDay.print(); // 클래스의 멤버 함수 호출 return 0; 
}

구조체의 멤버처럼 클래스의 멤버는 멤버 선택 연산자 . 를 통해 접근할 수 있다.

DateClass.printf()를 호출하면 m_yearDateClass.m_year로 해석한다.
연관된 객체가 암시적으로 멤버 함수에 전달이 되는 것을 암시적 개체라고 한다.

멤버 함수가 항상 작업할 클래스의 암시적 객체를 가지고 있다는 것이 중요

접근지정자

  • private : 클래스 내부에서만 접근 가능.
  • public : 클래스 내부뿐만 아니라 외부에서도 접근 가능.

해당 클래스를 제외한 클래스, 해당 클래스 멤버 함수를 제외한 다른 함수를 의미한다. 만약 클래스 외부에서 private 으로 선언된 변수에 접근하고 싶다면 gettersetter 함수를 이용해야한다.
private 로 선언하는 것은 정보를 감추거나 무분별한 변수의 값 변경을 막기 위해.

생성자(Constructor)

클래스의 모든 멤버 변수가 public인 경우 초기화 목록 또는 유니폼 초기화를 사용해 초기화할 수 있다. 그러나 멤버 변수가 private 인 경우에는 변수에 직접 접근할 수 없는 비공개 상태이므로 더는 위와 같은 방법으로는 클래스를 초기화할 수 없다.

생성자는 해당 클래스의 객체가 인스턴스화될 때 자동으로 호출되는 특수한 종류의 멤버 함수.
일반적으로 클래스의 멤버 변수를 적절한 기본값 또는 사용자 제공 값으로 초기화하거나 클래스를 사용하는 설정이 필요한 경우 사용된다.

생성자 정의 방법 규칙

  • 생성자 이름은 클래스와 이름이 같아야 한다
  • 생성자는 리턴 타입이 없다 (void도 아니다)
class ClassName
{
public:
	int i;
	int j;
	ClassName()
	{
		std::cout << "생성자 테스트 문구입니다\n";
	};
	ClassName(int i, int j)
	{
		//code
	};
	ClassName(int i)
	{
		//code
	};
	ClassName(int j)
	{
		//code
	};
	~ClassName();
};

class 선언 후 class명과 같은 이름의 생성자를 정의한다. 생성자는 여러 개의 매개변수를 가질 수 있고, 오버로딩을 지원하기 때문에 위와 같이 선언할 수 있다.

~Contact()

디폴트 생성자 앞에 ~을 붙인 형태. 매개 변수가 없다.

인스턴스의 사용이 끝나고 소멸될 때(메모리 반환) 자동으로 호출되는 코드

int main()
{
	ClassName name;
	ClassName *pName = new Name;
	return 0;
}
//=====[output]=====//
>> 생성자 테스트 문구입니다
>> 생성자 테스트 문구입니다

참고

프로그램 규칙

  1. 프로그램은 사용자의 커맨드 입력을 대기하고 있다.
  2. 유효한 명령은 EXITADDSEARCH 3가지이다.
    • EXIT : 프로그램 종료
    • ADD : Contact의 필드를 하나씩 입력받아, PhoneBook에 Contact를 하나 추가(연락처의 저장은 최대 8개까지 가능하며, 8개를 넘으면 가장 오래전에 저장된 연락처가 삭제)
    • SEARCH : index를 입력받아 해당 index에 대응되는 연락처를 출력
  3. 명령이 실행된 이후에는 다른 명령어 입력을 대기하고 있다.

Phonebook.cpp

int	main(void)
{
	std::string	cmd;
	Contact contact;

	std::cout << "📞 Hello~ This is soooh Phonebook" << std::endl;
	std::cout << "Save Contact   : ADD"<< std::endl;
	std::cout << "Search Contact : SEARCH"<< std::endl;
	std::cout << "Contact End    : EXIT"<< std::endl;
	while (true)
	{
		std::cout << "COMMAND        : ";
		std::cin >> cmd;
		if (cmd == "ADD")
			contact.Add();
		else if (cmd == "SEARCH")
			contact.Search();
		else if (cmd == "EXIT")
			return (0);
		else
			std::cout << "===== Wrong input =====" << std::endl;
		std::cin.clear();
		std::cin.ignore(1000, '\n');
		if (std::cin.eof())
			return (0);
	}
}

EXIT 입력하거나 eof 데이터가 없을 때까지 반복문
cin으로 입력 받으려고 할 때 EOF(End Of File)이라면 입력이 취소되며 이때 반환값은 true이다.
이를 이용해 파일이 종료될 때까지 입력 받는 코드를 작성할 수 있다. (터미널에서는 수동 입력으로 해야 함 ctrl + D) 참고

cin을 통해 문자와 숫자를 입력 받을 때 저장되는 방식이 다르다.

문자: 입력 버퍼에 저장 후 해당 값을 읽어 변수에 저장
숫자: 바로 변수에 저장 (버퍼가 필요없음)

예를 들어 숫자를 입력 받아 변수에 입력하는 도중에 문자가 입력되면 해당 버퍼에 저장이 되면 버퍼에 저장된 값이 정수형 변수에 저장하려고 하기 때문에 failbit를 설정해 버퍼에 값이 계속 남고 해당 함수를 예시로 들자면 ===== Wrong input ===== 이 반복적으로 나오게 된다.

cin.clear()를 통해 에러비트를 초기화하고, cin.ignore()를 통해 두 번째 인자인 개행으로 버퍼를 계속 비워주겠다는 뜻이다.

contact.cpp

void	Contact::Add()
{
	int	i;
	if (index == 8)
	{
		for(i = 1; i < index; i++)
			book[i - 1] = book[i];
		index--;
	}
	std::cout << "first name : ";
	std::cin >> book[index].first_name;
	std::cout << "last name : ";
	std::cin >> book[index].last_name;
	std::cout << "nickname : ";
	std::cin >> book[index].nickname;
	std::cout << "phone number : ";
	std::cin >> book[index].phone_number;
	std::cout << "darkest secret : ";
	std::cin >> book[index].darkest_secret;
	index++;
}

연락처는 최대 8 개까지 저장할 수 있고, 추가로 저장을 할 때 제일 최근에 저장된 연락처가 사라진다. 연락처의 갯수가 8 개라면 반복문을 통해 해당 번호의 앞 번호로 다시 저장 후 연락처를 저장한다.

std::string	LengthCheck(std::string str)
{
	if (str.length() >= 10)
	{
		str = str.substr(0, 9);
		str += ".";
	}
	return (str);
}

void	Contact::Search()
{
	int	i;

	i = 0;
	std::cout << "|";
	std::cout << std::setw(10) << "INDEX" << "|";
	std::cout << std::setw(10) << "First name" << "|";
	std::cout << std::setw(10) << "Last name" << "|";
	std::cout << std::setw(10) << "Nickname" << "|" << std::endl;
	for(i = 0; i < index; i++)
	{
		std::cout << "|";
		std::cout << std::setw(10) << i + 1 << "|";
		std::cout << std::setw(10) << LengthCheck(book[i].first_name) << "|";
		std::cout << std::setw(10) << LengthCheck(book[i].last_name) << "|";
		std::cout << std::setw(10) << LengthCheck(book[i].nickname) << "|" << std::endl;;
	}
	std::cout << "Enter the index of the name : ";
	std::cin >> i;
	i--;
	if (index == 0)
		std::cout << "oh.. Phonebook is empty" << std::endl;
	else if (i < index && i > 0)
	{
		std::cout << "Fist name      : " << book[i].first_name << std::endl;
		std::cout << "Last name      : " << book[i].last_name << std::endl;
		std::cout << "Nickname       : " << book[i].nickname << std::endl;
		std::cout << "PhoneNumber    : " << book[i].phone_number << std::endl;
		std::cout << "Darkest Secret : " << book[i].darkest_secret << std::endl;
	}
}

서브젝트 규정 중 연락처를 출력하는데 있어서 한정된 크기를 부여 받았고 해당 조건을 충족하기 위해 setw(크기)를 사용했다.
setw()는 매개 변수만큼 공간을 잡고 그 공간에 내용을 출력한다.

ex) std::setw(10) << “a” ⇒ | a| 10칸 확보 후 “a”를 출력.

연락처의 내용이 10 글자 이상일 때 9개만 출력 후 .으로 대체하는 조건이 있었다.
str.substr()으로 해당 문자열의 0부터 9번째의 글자만 출력하고 다음에 .을 출력하도록 했다.

profile
42Seoul -soooh ~ 2022.04

0개의 댓글