목차 ¶
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);
2.2.2. 4.2.3. Generating the report ¶
2.3. 4.3. Putting it all together ¶
~cpp #ifndef GURAD #define GUARD /* ... ... ... */ #endif










