클래스, 템플릿, 컨테이너, 반복자, 맵, Solid원칙, 스마트 포인터 등등 너무 많은 정보가 머리에 한번에 들어오니깐 내가 이것을 이해하고 있는것인지 아니면 추상적으로 머리에 용어만 들어가 있는것인지.. 우선 개념은 아리송하게 알 것 같은데 막상 코드로 어떻게 들어가는지 전혀 모르겠어서 너무 답답했다. 이럴땐 예제를 보면서 무작정 따라하며 배우는게 깨닫는게 있을거라는 마음에 무작정 각 개념들이 적용된 코드들을 찾아보았다. 그리고 무작정 따라하면서 이해하는게 좋을 것 같다고 판단했다. 종국에는 자유롭게 내가 원하는 작은 프로젝트를 만들 수 있는데 까지 빠르게 가는 것이 목표이다.
일단 첫 예제이다.
템플릿, map이 적용된 작은 예제를 한줄 한줄 이해하려고 하며 무작정 따라 적어보았다. 이해하는데만 하루 절반을 쓸 정도로 매우 오래 걸렸다.. 우선 범위 기반 for문, 함수뒤에 붙는 const, static.cast 명시적 형변환, map의 활용을 조금은 이해할 수 있었다.. 눈이 빠질것 같지만 그래도 조금씩 이해를 할 수 있는 것같아 기쁘다.
점점 이해에 가속이 붙을 수 있도록 많은 코드를 보고 실습을 해봐야겠다.
코드 설명란은 실습하면서 공부하며 제가 이해한 내용을 바탕으로 막 적은거라 오류가 있을 수 있습니다.
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <iomanip>
// 1. 학생 정보를 담는 클래스
class Student {
private:
std::string name; // 이름
int id; // 학번
std::vector<float> grades; // 성적 리스트
public:
Student() : name("Unknown"), id(0), grades() {}
// 생성자
Student(const std::string& name, int id, const std::vector<float>& grades)
: name(name), id(id), grades(grades) {
}
// 학번 반환
int getId() const {
return id;
}
// 이름 반환
std::string getName() const {
return name;
}
// 성적 리스트 반환
std::vector<float> getGrades() const {
return grades;
}
// 성적 리스트 수정
void setGrades(const std::vector<float>& newGrades) {
grades = newGrades;
}
// 학생 정보 출력
void displayInfo() const {
std::cout << "Name: " << name << ", ID: " << id << ", Grades: ";
for (float grade : grades) {
std::cout << grade << " ";
}
std::cout << std::endl;
}
};
// 2. 템플릿 함수: 평균 계산
template <typename T>
float calculateAverage(const std::vector<T>& values) {
T sum = 0;
for (const T& value : values) {
sum += value;
}
return static_cast<float>(sum) / values.size();
}
// 3. 메인 프로그램
int main() {
// 학생 관리 컨테이너 (학번 -> 학생 객체)
std::map<int, Student> studentMap;
// 학생 추가
studentMap[101] = Student("Alice", 101, { 85.0, 90.5, 78.0 });
studentMap[102] = Student("Bob", 102, { 92.0, 88.5, 79.0 });
studentMap[103] = Student("Charlie", 103, { 76.5, 80.0, 85.5 });
// 학생 정보 출력
std::cout << "All Students:" << std::endl;
for (const auto& pair : studentMap) {
pair.second.displayInfo();
}
// 특정 학생 검색 (학번으로)
int searchId = 102;
std::cout << "\nSearching for student with ID " << searchId << "..." << std::endl;
if (studentMap.find(searchId) != studentMap.end()) {
studentMap[searchId].displayInfo();
}
else {
std::cout << "Student with ID " << searchId << " not found." << std::endl;
}
// 특정 학생 성적 수정
std::cout << "\nUpdating grades for student with ID 102..." << std::endl;
if (studentMap.find(searchId) != studentMap.end()) {
studentMap[searchId].setGrades({ 95.0, 88.0, 84.0 });
studentMap[searchId].displayInfo();
}
// 전체 학생 성적 평균 계산
std::cout << "\nCalculating average grades for all students: " << std::endl;
for (const auto& pair : studentMap) {
const Student& student = pair.second;
float avg = calculateAverage(student.getGrades());
std::cout << "Student " << student.getName() << " (ID: " << student.getId()
<< ") Average: " << std::setprecision(2) << std::fixed << avg << std::endl;
}
return 0;
}
grades
를 () 로 초기화한 이유
grades()
는 vector<float>
의 기본 생성자를 호출하는 코드이다. vector
는 기본적으로 비어있는 상태로 초기화된다. 즉, grades()
는 빈vector<float>
를 생성하는 것과 같다.
매개변수를 받는 생성자가 있음에도 기본생성자를 두는 이유는 map
때문이다. map
과 같은 STL 컨테이너가 객체를 내부적으로 복사하거나 기본 생성자를 호출하는 경우가 있기 때문.
map
과 같은 STL 컨테이너는 데이터를 관리할 때 다음과 같은 동작을 한다.
int getId() const{return Id;}
{}
로 들어가기전에 const가 있는 것을 볼 수있는데 객체의 상태를 변경하지 않음을 보장하는 키워드 이다. 멤버 변수를 변경하지 않는다.void setGrade(const vector<float> newGrades) { grades = newGrades; }
grades
는 setGrade
함수 내에서 자유롭게 변경 가능하다. 여기서도 중요한점은 grades
멤버 변수가 private
내에서 선언되었음에도 변경가능하다는 것이다. 여튼 여기서는 매개변수로 오는 newGrades
만 수정이 불가능하다. for (float grade : grades) { ~ }
grades
는 vector<float>
형태의 컨테이너이다. grade
('s'없음)는 grades
벡터의 각 요소를 순차적으로 하나씩 받아오는 변수이다.grade
는 grades
의 현재 요소를 복사한 값이 된다.grades
배열의 첫 번째 요소부터 마지막 요소까지 순차적으로 grade
값에 대입한다.템플릿은 따로 정리해놔서 pass, 굳이 언급하자면 만약 특수화를 하려면 template <>
를 써야한다. calculateAverage
함수에서 알게된건 static_cast
이다. 깊게 들어가면 복잡하지만 일단 '임시적인 형 변환' 이라고 생각하면 된다. sum은 T
타입으로 계산되지만 결국 static_cast<float>
에 의해 임시적으로 floa
로 변환 된다. 따라서 return
값은 float
/ int
이므로 무조건 float
값을 반환한다.
const auto
는 변수의 자료형을 자동으로 추론하겠다는 것이다. 그리고 이후 자료형을 변경할 수 없다고 하는것. 주로 변수 선언할 때 많이 사용하고 매개변수에도 사용이 가능하나 C++14부터 사용 가능하다.
setprecision(자릿수)
, fixed
는 헤더파일 <iomanip>
로 사용 가능해지며 setprecision
만 있으면 정수+소수의 총 자릿수만큼, fixed
와 같이 쓰면 소수의 자릿수만큼 표기해준다.
studentMap
이 map(int, Student)
형이므로 studentMap
의 각 요소는 pair<const int, Student>
형태이다.
for (const auto& piar : studentMap)
에서 pair
는 studentMap
의 각 요소를 참조한다. 이때 pair
는 pair<const int, Student>
의 참조이다.pair.first
는 studentMap
의 키(int
타입)이고, pair.second
는 값(Student
타입)이다. const
가 붙어 있으므로 pair
를 통해 값을 수정할 수 없으며, 읽기전용이다.(Read only!)