공통 지침
- 컴파일
- c++을 사용하여 컴파일하며, -Wall -Wextra -Werror 플래그를 추가해야 합니다.
- -std=c++98 플래그를 추가했을 떄 컴파일이 정상적으로 이루어져야 합니다.
- 형식 및 클래스 이름 명명
- Class 이름은 UpperCamelCase 형식을 지켜야 합니다.
예시)
ClassName.hpp/ClassName.cpp ...- 따로 명시되어있지 않는 한, 모든 output 메시지는 new-line 으로 끝나야 하며, standard output 으로 출력되어야 합니다.
- Goodbye Norminette!
강제되는 코드 스타일은 더이상 없지만, 가독성 있는 코드 스타일링을 유지하세요.- 허용/금지 사항
- Standard library에 있는 모든 것은 사용 가능하되, 최대한 "C++ 스러운" 함수들을 사용하세요.
- 다른 외부 라이브러리는 사용 불가합니다. (C++11, Boost 등)
- printf(), alloc(), free() 는 사용이 금지됩니다.
- 따로 명시되어있지 않는 한, namespace <ns_name> 및 friend 키워드는 사용이 금지됩니다.
- STL의 사용은 Module 08, 09 에서만 사용이 가능합니다.
- 구현 시 주의사항
- (당연히) memory leak 은 없어야 합니다.
- Module 02~09 에서는 Orthodox Canonical Form을 지켜 클래스를 구현해야 합니다.
- 클래스의 헤더파일에 함수 구현 시 0점
- 구현 한 헤더파일은 독립적으로 쓰일 수 있어야 합니다. 의존성을 지키되, 중복 include 를 방지하세요.
- 기타
- 코드 분할 / 테스트 케이스 작성 등을 위해 추가로 파일을 얼마든지 제출 할 수 있습니다.
- 각 exercise 마다 있는 예시들은 가이드라인에 명시되지 않는 요구사항을 포함 할 수 있습니다.
- RTFM!!
다음과 같이 동작하는 프로그램을 작성하십시오.
$>./megaphone "shhhhh... I think the students are asleep..."
SHHHHH... I THINK THE STUDENTS ARE ASLEEP...
$>./megaphone Damnit " ! " "Sorry students, I thought this thing was off."
DAMNIT ! SORRY STUDENTS, I THOUGHT THIS THING WAS OFF.
$>./megaphone
* LOUD AND UNBEARABLE FEEDBACK NOISE *
$>
char ft_toupper(char c) {
if (c >= 'a' && c <= 'z') {
return c - ('a' - 65);
} else {
return c;
}
}
int main(int argc, char* argv[]) {
if (argc == 1) {
std::cout << "* LOUD AND UNBEARABLE FEEDBACK NOISE *" << std::endl;
return 0;
}
for (int i = 1; i < argc; i++) {
for (size_t j = 0; j < strlen(argv[i]); ++j) {
argv[i][j] = ft_toupper(argv[i][j]);
}
std::cout << argv[i];
}
std::cout << std::endl;
return 0;
}
toupper
함수를 사용해도 되지만, C 과제의 버릇대로 직접 구현해서 써버렸다.간단한 전화번호부처럼 작동하는 프로그램을 작성하십시오.
- 두개의 클래스를 구현해야 한다.
1. PhoneBook
- 연락처(contact)의 배열
- 최대 8개의 연락처를 저장할 수 있으며, 9번째 추가 시 가장 오래된 것 부터 대체됨.
- 동적할당은 금지
2. Contact
- PhoneBook 의 연락처.
- 전화번호부와 연락처는 반드시 클래스에서 인스턴스화해서 사용해야 한다. 클래스의 구현은 자유지만, 클래스 내부에서만 사용할 것은 private, 외부에서 사용할 것은 public으로 구현해야한다.
- 프로그램 시작 시 전화번호부는 비어있는 상태여야 하며, 유저는 프롬프트를 통해 ADD, SEARCH, EXIT 중 한가지 명령어를 입력 할 수 있다.
- ADD - 신규 연락처를 추가
- 명령어 실행 시 유저는 한번에 하나의 field를 입력하게끔 프롬프트를 받는다. 입력이 전부 완료되면 연락처를 전화번호부에 추가한다.
- fields: first name, last name, nicknanme, phone number, darkest secret
필드는 비어있으면 안된다.- SEARCH - 특정 연락처 정보 검색 및 출력
- 저장된 모든 연락처의 index, first name, last name, nickname을 4개의 열로 나누어 표시한다.
- 각 열의 너비는 10자이며, 파이프 기호 ('|') 로 구분된다.
텍스트는 오른쪽 정렬.
10자를 넘어가면 truncate 하고 마지막 글자를 점 ('.') 으로 대체해야 한다.- 유저에게 한번 더 index를 입력받는다.
잘못되었거나 범위를 벗어난 입력 시 적절한 처리를 해야한다.
유효한 index일 경우 한줄에 하나씩 해당 연락처의 모든 field를 출력한다.- EXIT
- 프로그램이 종료되며 모든 연락처가 소실된다.
- 이외의 모든 명령어는 무시하며, 프롬프트 상태가 유지되어야 한다.
PhoneBook.hpp
#ifndef PHONEBOOK_HPP
# define PHONEBOOK_HPP
#include "Contact.hpp"
#include <iomanip>
class PhoneBook{
public:
PhoneBook();
~PhoneBook();
void add_contact();
void search_contact();
private:
Contact contacts[8];
int index;
int n_contacts;
};
#endif
Public
멤버는 클래스 인스턴스를 가지는 / 가르키는 어떤 코드에서든 접근이 가능하다.Private
멤버는 클래스 내부 및 friend
끼리만 접근 할 수 있다.public
멤버는 클래스의 인터페이스(전반적인 틀)을 구성하는 속성을 정의할 때 사용되고, private
은 클래스의 작동 방식을 정하는 멤버들에 붙는다 (멤버 함수 등).constructor
)와 소멸자(destructor
).delete
해주지 않는 한 일반적으로 소멸 단계는 프로그램 종료 시다. 보통 동적으로 할당한 오브젝트들을 해제(delete)하는 단계.PhoneBook.cpp
#include "PhoneBook.hpp"
PhoneBook::PhoneBook(){
index = 0;
n_contacts = 0;
}
PhoneBook::~PhoneBook() {}
void PhoneBook::add_contact(){
Contact contact;
std::string input;
if (index == 8)
index = 0;
std::cout << "Enter a first name" << std::endl;
std::getline(std::cin, input);
contact.set_first_name(input);
std::cout << "Enter a last name" << std::endl;
std::getline(std::cin, input);
contact.set_last_name(input);
std::cout << "Enter a nickname" << std::endl;
std::getline(std::cin, input);
contact.set_nickname(input);
std::cout << "Enter a phone number" << std::endl;
std::getline(std::cin, input);
contact.set_phone_number(input);
std::cout << "Enter a darkest secret" << std::endl;
std::getline(std::cin, input);
contact.set_darkest_secret(input);
if (std::cin.eof()){
std::cout << "Input cancelled" << std::endl;
return ;
}
contacts[index] = contact;
index++;
if (n_contacts < 8)
n_contacts++;
}
void print_cut_str(std::string str){
if (str.length() < 11)
std::cout << "|" << std::setw(10) << str;
else
std::cout << "|" << str.substr(0, 9) + ".";
}
void PhoneBook::search_contact() {
int idx;
if (n_contacts == 0){
std::cout << "The phonebook is currently empty." << std::endl;
return ;
}
for (int i = 0; i < n_contacts; i++){
std::cout << std::setw(10) << i + 1;
print_cut_str(contacts[i].get_first_name());
print_cut_str(contacts[i].get_last_name());
print_cut_str(contacts[i].get_nickname());
std::cout << "|" << std::endl;
}
std::cout << "\nEnter an index of the entry to display" << std::endl;
std::cin >> idx;
if (std::cin.eof()){
std::cout << "Input cancelled" << std::endl;
return ;
}
if (std::cin.fail() || idx < 1 || idx > n_contacts){
std::cout << "Error: invalid contact index" << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return ;
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "-------------------------" << std::endl;
std::cout << "First name: " << contacts[idx - 1].get_first_name() << std::endl;
std::cout << "Last name: " << contacts[idx - 1].get_last_name() << std::endl;
std::cout << "Nickname: " << contacts[idx - 1].get_nickname() << std::endl;
std::cout << "Phone Number: " << contacts[idx - 1].get_phone_number() << std::endl;
std::cout << "Darkest secret: " << contacts[idx - 1].get_darkest_secret() << std::endl;
std::cout << "-------------------------" << std::endl;
}
std::cin >> input;
위 처럼 받아도 되지만,
이렇게 하면 공백으로 나눠진 입력을 여러개의 입력으로 받아버린다.
Ctrl+D
등으로 프롬프트 종료 시 적절한 처리를 해줘야 한다.cin.clear();
: cin의 에러 플래그를 비워준다. 안그러면 켜져있는 채로 메인으로 다시 돌아간다.cin.ignore(streamsize n, int delim);
: cin 버퍼를 n 만큼, 또는 delim을 만날때까지 비워준다. 코드에서 크기는 저장되어있는 input stream 입력의 최대 크기로 지정했다.Contact.hpp
#ifndef CONTACT_HPP
# define CONTACT_HPP
class Contact {
public:
Contact();
~Contact();
std::string get_first_name() const;
std::string get_last_name() const;
std::string get_nickname() const;
std::string get_phone_number() const;
std::string get_darkest_secret() const;
void set_first_name(std::string str);
void set_last_name(std::string str);
void set_nickname(std::string str);
void set_phone_number(std::string str);
void set_darkest_secret(std::string str);
private:
std::string first_name;
std::string last_name;
std::string nickname;
std::string phone_number;
std::string darkest_secret;
};
#endif
getter
와 setter
에 대해 정리하기 위해 가져와봤다.std::string Contact::get_first_name() const{
return (this->first_name);
}
void Contact::set_first_name(std::string str){
this->first_name = str;
}
private
멤버들은 기본적으로 클래스 외부에서 접근 할 수 없기 때문에 getter
와 setter
를 사용해서 가져오거나 설정해줘야한다.private
멤버들을 가지고 있는 같은 클래스의 멤버 함수를 사용해서 접근한다는 점, 어느정도 통제된 인터페이스 내에서만 할 수 있다는 점, 바로 접근이 안되고 함수를 통해 접근함으로써 예외처리를 할 수 있다는 점 등에서 최소한의 안정성이 보장된다.private
멤버들을 지정하는 일을 캡슐화(encapsulation)이라고 부른다. 어찌됐든 민감한 정보를 보호하는 개념이니 getter
와 setter
는 꼭 필요할 때 만 쓰도록 하자.main.cpp
#include "PhoneBook.hpp"
int main(void){
PhoneBook pb;
std::string cmd;
while(true){
std::cout << "Enter a command: [ADD / SEARCH / EXIT]" << std::endl;
std::getline(std::cin, cmd);
if (cmd == "EXIT" || std::cin.eof()){
exit(0);
}
else if (cmd == "ADD"){
pb.add_contact();
}
else if (cmd == "SEARCH"){
pb.search_contact();
}
else
std::cout << "Error: Invalid command" << std::endl;
if (std::cin.eof()){
clearerr(stdin);
std::cin.clear();
}
}
return (0);
}