C++에서는 같은 타입의 원소를 순서대로 저장할 수 있는 여러 컨테이너를 제공해요.
크기가 고정된 컨테이너를 위한 std::array가 있고, 크기를 동적으로 조절할 수 있는 std::vector도 있어요.
배열이나 벡터를 선언할 때는 그 컨테이너가 담을 원소의 타입을 지정해야 해요. 배열은 크기도 필요해요. 두 컨테이너 타입을 각각 어떻게 초기화하는지 다음 예제를 살펴봐요.
#include <array>
#include <string>
// std::array<element_type, size> variable_name {list of elements}
std::array<std::string, 3> indie_rock {"yeah", "yeah", "yeah"};
// indie_rock contains the elements "yeah" three times
벡터는 나중에 더 커질 것을 대비해 메모리를 미리 확보하기 때문에 보통 더 많은 공간이 필요해요. 크기는 지정하지 않아도 돼요.
#include <vector>
// std::vector<element_type> variable_name {list of elements}
std::vector<int> countdown {3, 2, 1};
// countdown contains the elements 3, 2 and 1
참고: 템플릿 개념의 정확한 원리는 아직 몰라도 돼요. 템플릿은 실라버스 트리에서 더 아래쪽에 자체 개념으로 다뤄져요.
벡터와 배열은 원소에 접근하는 함수를 공유해요.
멤버 함수 front와 back으로 컨테이너의 첫 번째와 마지막 원소를 가져올 수 있어요.
특정 원소에 접근할 때는 at과 [] 연산자도 쓸 수 있어요.
countdown[0];
// => 3
countdown.at(2);
// => 1
countdown[1] = 4;
// countdown now contains 3, 4 and 1
indie_rock.back() = "yeahs";
// indie_rock is now "yeah", "yeah", "yeahs"
참고: 컨테이너의 범위를 벗어난 위치를 고르면
[]는 정의되지 않은 동작을 일으킬 수 있어요.at함수는 예외를 발생시키는데, 길게 보면 이쪽이 더 편할 수 있어요.
vector는 크기가 고정되어 있지 않아서 원소를 추가하거나 제거할 수 있어요.
이를 위해 자주 쓰이는 함수로는 emplace_back과 pop_back이 있어요.
std::vector<std::string> witches {"Holly", "Alyssa", "Shannen"};
witches.pop_back();
// Shannen is no longer with the witches
witches.emplace_back("Rose");
// Rose has joined the team
멤버 함수 empty로 컨테이너가 비어 있는지 확인할 수 있어요.
원소의 개수를 알고 싶다면 size를 사용하면 돼요.
학생들의 시험을 채점하는 조교라고 해요. 결과를 일일이 손으로 관리하는 건 점점 지루해지고 실수하기도 쉬워져요. 그래서 반의 결과를 세고 계산해 줄 함수 몇 개를 만들어서, 일을 조금 더 흥미롭게 만들어 보기로 해요.
시험 문제에는 "부분 점수"를 줄 수 있지만, 전체 시험 점수는 ints여야 해요.
그래서 반 점수로 다른 작업을 하기 전에, 먼저 점수들을 하나씩 살펴보면서 double 점수를 int로 바꿔야 해요.
student_scores의 vector를 받는 round_down_scores() 함수를 만들어요.
학교에서 요청한 방식은 소수점 이하를 버리는 거예요.
다행히 C++에서는 static_cast로 한 타입을 다른 타입으로 변환할 수 있어요.
double에서 int로의 static_cast 변환은 소수점 뒤의 자릿수를 잘라내요.
이 함수는 입력 vector를 받아서, 모든 점수가 int로 변환된 새 vector를 return해야 해요.
std::vector<double> student_scores {90.33, 40.5, 55.44, 70.05, 30.55, 25.45, 80.45, 95.3, 38.7, 40.3};
round_down_scores(student_scores)
// => {90, 40, 55, 70, 30, 25, 80, 95, 38, 40}
같은 결과를 얻는 다른 방법들도 있지만, _캐스트_는 흔히 볼 수 있는 방식이에요.
int new_PI{static_cast<int>(3.14) + static_cast<int>(0.001592653589)};
// new_PI is 3.
시험을 채점하다 보니, 몇몇 학생이 기대만큼 잘하지 못했다는 걸 알게 됐어요. 그런데 다른 생각에 빠져서, 학생이 _몇 명_인지 정확히 적어두는 걸 깜빡했어요.
student_scores의 vector를 받는 count_failed_students() 함수를 만들어요.
이 함수는 합격 점수를 받지 못한 학생 수를 세고, 그 수를 정수로 반환해야 해요.
학생이 시험에서 합격하려면 40점보다 높은 점수를 받아야 해요.
count_failed_students({90,40,55,70,30,25,80,95,38,40});
// => 5
함께 일하는 선생님은 숫자 점수뿐만 아니라 학점도 매기는 걸 좋아해요. 학생들이 시험에서 100점을 받는 일은 드물기 때문에, "학점"의 각 하한은 시험에서 나온 최고 점수를 기준으로 계산하고, 최고 점수와 <= 40인 낙제 기준 사이를 균등하게 나눠요.
시험의 "최고" 점수를 매개변수로 받아서, 각 "미국식" 학점 구간에 대한 하한 점수를 담은 array(vector가 아니에요)를 반환하는 letter_grades() 함수를 만들어요: ["D", "C", "B", "A"].
/*Where the highest score is 100, and failing is <= 40.
"F" <= 40
41 <= "D" <= 55
56 <= "C" <= 70
71 <= "B" <= 85
86 <= "A" <= 100
*/
letter_grades(100);
// => {41, 56, 71, 86}
/*Where the highest score is 88, and failing is <= 40.
"F" <= 40
41 <= "D" <= 52
53 <= "C" <= 64
65 <= "B" <= 76
77 <= "A" <= 88
*/
letter_grades(88);
// => {41, 53, 65, 77}
시험 점수가 내림차순으로 있고, 그에 대응하는 학생 이름도 (시험 점수 순서대로 정렬되어) 있어요. 각 학생의 이름을 시험 점수와 연결해서 반 전체 순위를 출력하고 싶어요.
student_scores와 student_names를 매개변수로 받는 student_ranking() 함수를 만들어요.
student_names vector의 각 학생 이름을 student_scores vector의 점수와 연결해요.
각 인자 vector는 점수가 높은 순에서 낮은 순으로 정렬되어 있다고 가정해도 돼요.
이 함수는 <rank>. <student name>: <student score> 형식의 문자열 vector를 반환해야 해요.
std::vector<int> student_scores {100, 99, 90, 84, 66, 53, 47};
std::vector<std::string> student_names {"Joci", "Sara","Kora","Jan","Indra","Bern", "Fred"};
student_ranking(student_scores, student_names)
// =>
// {"1. Joci: 100", "2. Sara: 99", "3. Kora: 90", "4. Jan: 84", "5. Indra: 66", "6. Bern: 53", "7. Fred: 47"}
시험에서 100점이라는 "완벽한" 점수는 드물지만, 적어도 한 명의 학생이 그 점수를 받았는지 아는 건 흥미로워요.
student_scores와 student_names를 매개변수로 받는 perfect_score() 함수를 만들어요.
목록은 4번 과제와 같아요.
이 함수는 시험에서 100점을 받은 학생의 첫 번째 <name>을 (문자열로) return해야 해요.
100점이 하나도 없으면 빈 문자열 ""을 반환해야 해요.
perfect_score({"Nur", "Tony", "Fatima"}, {90, 80, 100});
// => "Fatima"
perfect_score({"Nur", "Tony"}, {90, 80});
// => ""
Exercism에 가입하고 C++ 트랙을 개념 19개연습 문제 100개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.