목차 ¶
2. Chapter 4 Organizing programs and data ¶
- 어떤 종류의 문제를 푼다.
- 다른 것들과 독립적이다.
- 이름을 가지고 있어야 한다.
3장까지 봤던 것은 첫번째것만 있고, 나머지 것은 없다. 프로그램이 작을때는 별로 상관없지만, 커지기 시작하면서부터 2,3번째 것이 결여되면, 나중엔 제어가 불가능해진다. C++에서는 다른 언어와 마찬가지로 함수 + 자료구조를 제공함으로써, 프로그램을 구조화시킬수 있는 방법을 제공한다. 또한 함수 + 자료구조를 묶어서 가지고 놀 수 있는 class라는 도구를 제공해준다.(Chapter9부터 자세히 살펴본다.)
그러므로 이번장부터는 프로그램을 나눠서, 서로 다른 파일에 저장, 컴파일하는 법과, 나중에 합치는 법 등을 공부할 것이다.
2.1. 4.1 Organizing computations ¶
~cpp cout << "Your final grade is " << setprecision(3) << 0.2 * midterm + 0.4 * final + 0.4 * median << setprecision(prec) << endl;이렇게 생겼다. 둘째줄의 등급 계산하는 부분을 다음과 같이 함수로 뽑아낼 수가 있다.
~cpp double grade(double midterm, double final, double homework) { return 0.2 * midterm + 0.4 * final + 0.4 * homework; } /* ... */ int main() { /* ... */ cout << "Your final grade is " << setprecision(3) << grade(midterm, final, sum / count) << setprecision(prec) << endl; return 0; }
~cpp return_type function_name(parameter lists...) { 함수 내에서 할 일들 }이렇게 하면 된다.
2.1.1. 4.1.1. Finding medians ¶
~cpp double median(vector<double> vec) { typedef vector<double>::size_type vec_sz; vec_sz size = vec.size(); if (size == 0) throw domain_error("median of an empty vector."); sort(vec.begin(), vec.end()); vec_sz mid = size/2; return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid]; }
2.1.2. 4.1.2 Reimplementing out grading policy ¶
~cpp double grade(double midterm, double final, const vector<double>& hw) { if(hw.empty()) // 책에서는 hw.size()==0 이라고 되어 있지만 // empty()메소드를 호출하는 것이 더 효율적이라고 하더군요. throw domain_error("student has done no homework"); return grade(midterm, final, median(hw)); }
- const vector<double>& hw : 이것을 우리는 double형 const vector로의 참조라고 부른다. reference라는 것은 어떠한 객체의 또다른 이름을 말한다. 또한 const를 씀으로써, 저 객체를 변경하지 않는다는 것을 보장해준다. 또한 우리는 reference를 씀으로써, 그 parameter를 복사하지 않는다. 즉 parameter가 커다란 객체일때, 그것을 복사함으로써 생기는 overhead를 없앨수 있는 것이다.
~cpp vector<double> homework; vector<double>& hw = homework; // hw는 homework와 같다. 즉, 이름은 다르지만, hw를 고치면 homework도 같이 고쳐진다. 왜냐? 같으니까
- grade() function : 우리는 아까 grade라는 함수를 만들었었다. 그런데 이번에 이름은 같으면서 parameter는 조금 다른 grade()를 또 만들었다. 이런게 가능한가? 가능하다. 이러한 것을 함수의 overloading이라고 한다. 함수 호출할때 어떤게 호출될까는 따라가는 parameter lists에 의해 결정된다.
- exception : 사용자에게 무엇이 잘못되었는가를 알려주는 exception을 던져준다.
2.1.3. 4.1.3 Reading homework grades ¶
~cpp istream& read_hw(istream& in, vector<double>& hw) { double x; while(in >> x) // 차차 살펴볼테지만 이건 잘못되었다. hw.push_back(x); } read_hw(cin, homework); // 호출
- hw가 넘어오면서 hw안에 아무것도 없다는 것을 보장할수 있겠는가? 먼저번에 쓰던 학생들의 점수가 들어있을지도 모른다. 확실하게 없애주기 위해 hw.clear()를 해주자.
- 저 while 루프가 언제 멈출지 알수 있는가? 파일의 끝에 도달했거나, 입력받은게 등급이 아닐때일 것이다.
- 파일의 끝에 도달했다는 것 = 읽기 실패
- 입력받은게 등급이 아닐때(점수를 입력해야 되는데 이상한 것을 입력했을때) istream 객체 in은 실패 상태가 된다. 또한 그것을 읽지 않는다. 파일의 끝에 도달한것 처럼...
- 파일의 끝에 도달했다는 것 = 읽기 실패
- 이 상황을 해결하기 위해, 그냥 in객체의 상태를 초기화해줘버리자. in.clear() 모든 에러 상태를 리셋시켜준다.
~cpp istream& read_hw(istream& in, vector<double>& hw) { if(in) { hw.clear(); double x; while(in >> x) hw.push_back(x); in.clear(); } return in; }
2.1.4. 4.1.4 Three kinds of function parameters ¶
- median 함수를 보면, vector<double> 파라메터가 참조로 넘어가지 않는다. 학생수가 많아질수록 매우 큰 객체가 될텐데 낭비가 아닌가? 하고 생각할수도 있지만, 어쩔수 없다. 함수 내부에서 소팅을 해버리기 때문에 참조로 넘기면, 컨테이너의 상태가 변해버린다.
- grade 함수를 보면, vector는 const 참조로 넘기고, double은 그렇지 않다. int나 double같은 기본형은 크기가 작기 때문에, 그냥 복사로 넘겨도 충분히 빠르다. 뭐 값을 변경해야 할때라면 참조로 넘겨야겠지만... const 참조는 가장 일반적인 전달방식이다.
- read_hw 함수를 보면, 복사는 하지 않고, 그 값을 변경하기 위해 참조만 썼다.
2.1.5. 4.1.5 Using functions to calculate a student's grade ¶
~cpp try { // 하다가 예외 발생하면, 중단하고 } catch(domain_error) { // 이리로 온다. 만약에 try 안에서 예외 안 뜨면 catch 안은 수행안한다. }
여기까지 최종소스
~cpp #include <ios> #include <iomanip> #include <iostream> #include <string> #include <vector> #include <algorithm> #include <stdexcept> using namespace std; double grade(double midterm, double final, double homework); double grade(double midterm, double final, const vector<double>& hw); double median(vector<double> vec); istream& read_hw(istream& in, vector<double>& hw); int main() { cout << "Please enter your first name: "; string name; cin >> name; cout << "Hello, " << name << "!"; cout << "Please enter your midterm and final exam grades: "; double midterm, final; cin >> midterm >> final; cout << "Enter all your homework grades, " "follewd by end-of-file: "; vector<double> homework; read_hw(cin, homework); try { double final_grade = grade(midterm, final, homework); streamsize prec = cout.precision(); cout << "Your final grade is " << setprecision(3) << final_grade << setprecision(prec) << endl; } catch(domain_error) { cout << endl << "You must enter your grades. " "Please try again." << endl; return 1; } return 0; } double grade(double midterm, double final, double homework) { return 0.2 * midterm + 0.4 * final + 0.4 * homework; } double grade(double midterm, double final, const vector<double>& hw) { if(hw.size() == 0) throw domain_error("Student has done no homework"); return grade(midterm, final, median(hw)); } double median(vector<double> vec) { typedef vector<double>::size_type vec_sz; vec_sz size = vec.size(); if(size == 0) throw domain_error("median of an empty vector"); sort(vec.begin(),vec.end()); vec_sz mid = size / 2; return size % 2 == 0 ? (vec[mid] + vec[mid-1]) / 2 : vec[mid]; } istream& read_hw(istream& in, vector<double>& hw) { if(in) { hw.clear(); double x; while(in >> x) hw.push_back(x); in.clear(); } return in; }
2.2.1. 4.2.1 Keeping all of a student's data together ¶
~cpp struct Student_info { string name; double midterm, final; vector<double> homework; };
~cpp istream& read(istream& is, Student_info& s) { is >> s.name >> s.midterm >> s.final; read_hw(is, s.homework); return is; }
- is >> 을 보면, string을 읽었다가 double을 읽기도 한다. 이게 가능한 이유는 오버로딩 때문이다.
- Student_info형 변수 s의 값을 변경시키기 위해 참조로 넘겨줬다.
~cpp double grade(const Student_info& s) { return grade(s.midterm, s.final, s.homework); }
~cpp sort(vec.begin(), vec.end());
- 저 vec은 double형 값을 담고 있었기 떄문에, 순서대로 sort하면 되었었다. 하지만 우리가 만든 Student_info는 어떻게 sort를 할까?
~cpp sort(students.begin(), students.end()); // 과연? #!$%#@^#@$#
- 무엇을 기준으로 sort를 할것인가? 이름? midterm? final? 알수가 없다. 따라서 우리는 predicate라는 것을 정의해 주어야 한다. 다음과 같이 해주면 된다.
~cpp bool compare(const Student_info& x, const Student_info& y) { return x.name < y.name; } sort(students.begin(), students.end(), compare);