프로그램과 데이터의 구조화 organizing programs and data
DESCRIPTION
Chapter 4. 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA. 4 장에서는 …. 3.2.2 절의 프로그램이 생각했던 것보다 긴가요 ? 하지만 vector 나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다 . 표준 라이브러리 의 힘입니다 . 프로그램이 커지면 , 프로그램을 여러 독립적인 부분으로 분할하고 개별 관리 C++ 는 이런 방법을 제공 : 함수 와 자료구조 - PowerPoint PPT PresentationTRANSCRIPT
프로그램과 데이터의 구조화ORGANIZING PROGRAMS AND DATA
Chapter 4
4 장에서는…
3.2.2 절의 프로그램이 생각했던 것보다 긴가요 ? 하지만
vector 나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다 . 표준 라이브러리의 힘입니다 .
프로그램이 커지면 , 프로그램을 여러 독립적인 부분으로 분할하고 개별 관리 C++ 는 이런 방법을 제공 : 함수와 자료구조 그리고 함수와 자료구조를 하나의 이름으로 묶는 클래스 제공
( 클래스 만드는 법은 9 장에서 공부 )
4 장은 , 함수와 자료구조 (struct) 를 이용하여 프로그램의 구조화
방법 프로그램을 여러 파일로 나누어 독립적으로 컴파일 하는 방법
2
4.1 함수를 이용한 구조화하기
만일 같은 계산을 하는 코드가 여러 곳에 있다면… 성적 계산 방식이 바뀌면… 수십만 라인의 프로그램의 곳곳에 퍼져 있다면 , 재앙…
함수를 사용 계산을 매번 반복하지 않고 , 함수를 만들고 , 그 함수를 이용한다 변경하고 싶을 때 바꾸기가 쉽다
3
int main( ){ … cout …<< 0.2*midterm+0.4*final+0.4*sum/count ; … … cout …<< 0.2*midterm+0.4*final+0.4*sum/count ;
return 0;}
함수의 구조
ret-type: 함수가 결과로 리턴하는 값의 타입 function-name: 함수의 이름 param-decls: 함수의 매개변수 . 여러 개 일 때 콤마 (,) 로
구분
함수는 이름 , 매개변수 , 몸체 , 그리고 리턴 타입으로 구성 함수 호출을 할 때 , 인자는 개수와 타입이 일치해야 한다 값에 의한 호출 (call-by-value): 매개변수가 지역변수로
생성되고 , 인자의 값이 1:1 로 복사된다
4
ret-type function-name (param-decls) { // 함수 정의// 함수 본체
}
함수 만들기 : 성적 계산 함수
성적계산 grade() 함수
5
int main( ){ … cout …<< grade(midterm, final, sum/count) ; … … cout …<< grade(midterm, final, sum/count) ;
return 0;}
매개변수 (parameter)
인자 (argument)
double grade(double midterm, double final, double homework){ return 0.2*midterm+0.4*final+0.4*homework;}
grade() 함수 호출
매개변수를 지역변수로 보고 ,
인자 전달 과정을 지역변수 초기화로 볼 수 있음
함수 만들기 : 중앙값 찾기
무엇을 받고 무엇을 리턴 해야 할까 ? : 함수 설계의 첫 단계
여러 값을 저장한 vector 객체를 받고 , 중앙값을 리턴
6
// 이 함수를 호출하면 vector 인자 전체를 복사한다는 것에 유념 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=siz/2; return size%2==0? (vec[mid]+vec[mid-1])/2: vec[mid];}
매개변수 복사에 시간이 소요됨 vector 에 담겨있는 값이 많을수록 많은 시간이 걸린다 sort() 에 의해 값이 변경되므로 , 시간이 걸리더라도 복사가 유용함
예외 처리 (Exception Handling)
오류가 났을 때 어떻게 대처해야 하나 ? 3.2.2 절 (p.98) 에서는 그곳에서 즉시 오류 처리했음 여기서는 예외상황 (exception) 을 throw( 발생 ) 시킴
domain_error 객체를 throw 했음 : <stdexcept> 헤더에 정의됨
그러면 median() 을 호출한 곳에서 throw 된 것을 catch 하여 적절히 오류 처리함
예외가 발생하면 , throw 위치에서 실행을 멈추고 , 예외상황 객체 (exception
object) 를 가지고 , 나머지 부분을 건너뜀 . 이 예외상황 객체는 호출한 곳에서 적절히 대처하는데 필요한
정보를 담고 있음
7
함수 만들기 : 성적 계산식 다시 구현하기
또 다른 grade() 함수 여러 과제 성적을 받아 , 중앙값을 계산하고 , 그것으로 최종
성적을 계산하는 기능 이전 grade() 함수는 이미 계산된 중앙값 또는 평균값을
받았음
8
// 중간 , 기말 , 과제성적을 이용하여 전체 성적 계산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));}
몇 가지 생각해 볼 것은… 이전에 이미 사용한 grade() 라는 함수 이름을 중복해 써도 되나 ? 세 번째 매개 변수에 있는 & 는 무엇인가 ? grade() 가 grade() 를 호출했네 ? 순환 호출 (recursive call)
인가 ?
레퍼런스 (reference) 변수
9
레퍼런스 또는 참조 변수는 객체에 대한 또 다른 이름
vector<double> homework;vector<double>& hw=homework; // hw 는 homework 에 대한
별칭vector<double>& hw1 = hw; // hw1 은 ”
const vector<double>& chw=homework; // chw, chw1 는 homework 에 대한
const vector<double>& chw1=chw; // 읽기전용 별칭
참조에 의한 호출 (call-by-reference) 함수 매개변수에 레퍼런스 & 를 넣으면 , 해당 인자를 복사하지
않으므로 , 복사에 따른 간접비용 없음 const 를 넣으면 인자의 값을 바꾸지 않겠다고 구현시스템에
알려줌
call by reference
참조에 의한 호출 예 .
10
void foo(int & r){ r = 10;}
void main(){ int x = 20; foo(x); cout << x;}
// 옳게 작동 ( 레퍼런스 버전 )void swap(int& a, int& b){ int tmp;
tmp=a; a=b; b=tmp;}
// 옳게 작동 안 함void swap(int a, int b){ int tmp;
tmp=a; a=b; b=tmp;}
swap() 함수 예
함수오버로딩 (overloading)
오버로딩 : 함수 오버로딩과 연산자 오버로딩
함수 오버로딩 여러 함수가 같은 이름을 가질 수 있다 . 예 . 두 개의 서로 다른 grade() 함수
11
double grade(double midterm, double final, double homework){ …}
double grade(double midterm, double final, const vector<double>& hw){ …}
호출되었을 때 , 컴파일러는 어떤 것을 기동할지 어떻게 알지 ?
함수 오버로딩 예 함수 오버로딩에서는 두 함수의 매개변수의 순서 , 개수 또는
타입이 달라야 한다 .
12
void foo(int) { ... }; // 1void foo(double) { ... }; // 2void foo(int, int) { ... }; // 3void foo(int, double) { ... }; // 4int foo(int); // Compiler Error! void main(){ foo(3); // Calls 1 foo(5, 3.14); // Calls 4}
과제 성적 읽기
읽어 들이는 기능을 함수로 만들어 보면 , 리턴해야 할 것이 두 가지
읽은 값을 리턴 해야 하고 , 또한 제대로 읽었는지 ( 오류 여부 ) 리턴 해야 함 .
함수는 하나만 리턴 할 수 있다 레퍼런스 매개 변수를 이용하여 읽은 값을 리턴 하자 !
함수의 골격은 아래와 같이…
13
// 입력 스트림으로부터 vector<double> 에 과제 성적을 읽어 들임istream& read_hw( istream& in, vector<double>& hw){ // 채워야 할 곳… return in;}
int main(){ vector<double> homework; if( read_hw(cin, homework) ) { /*… */ } }
과제 성적 읽기 : 함수 구현
처음 시도해 본 코드 . 아래와 같이 하면 될까 ?
문제점은 hw 가 깨끗하다는 보장이 있나 ? (hw 는 함수를 호출한 곳에서 넘겨준
것 ) 이전에 사용되어 벡터에 성적이 담겨있다면 ?
만일 입력 도중에 오류가 난다면 ? 이후에 다른 함수에서 일어나는 입력 기능이 제대로 될까 ?
입력 실패 : (1) EOF 만났을 때 (2) 성적이 아닌 값을 입력되었을 때 in 이 깨끗할까 ? 만일 in 이 이전에 사용되어 무엇이 남아 있다면 ?
입력 실패 후 in 의 내부에 있는 오류 상태 리셋 필요 다음 학생 성적 입력 전에 정리 필요
14
istream& read_hw(istream& in, vector<double>& hw){ double x; while( in>>x ) hw.push_back(x);
return in;}
과제 성적 읽기 : 함수 구현
read_hw() 완전한 코드
15
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;}
hw벡터 : 빈 벡터로 시작하도록 함
istream 객체 : 오류상태 리셋 , 입력 작업을 계속하도록 함
http://www.cppreference.com/wiki/io/eof
함수 매개변수 양식 세가지 call-by-value:
복사하면 간접비용 든다 . 하지만 그래야 할 경우가 있다 . call-by-reference:
이 함수에서 인자의 값을 변경할 의도가 있다 . 인자는 lvalue( 변수 ) 이어야 함
const 인 call-by-reference: 이 함수에서 인자의 값을 변경하지 않고 , 읽기만 한다 .
어떤 경우에 무엇을 쓸지 판단해야 한다 .
16
double median( vector<double> vec) { …}
double grade(double midterm, double final, double homework) { …}
double grade(double midterm, double final, const vector<double>& hw) { …}
istream& read_hw( istream& in, vector<double>& hw) { …}
함수를 이용한 성적계산 - 전체프로그램
17
#include <algorithm>#include <iomanip>#include <iostream>#include <stdexcept>#include <string>#include <vector>using namespace std;
// compute the median of a `vector<double>'// note that calling this function copies // the entire argument `vector'double median(vector<double> vec){ typedef std::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];}
18
// compute a student's overall grade from midterm and // final exam grades and homework gradedouble grade(double midterm, double final, double homework){ return 0.2 * midterm + 0.4 * final + 0.4 * homework;}
// compute a student's overall grade from midterm and final// exam grades and vector of homework grades.// this function does not copy its argument, because `median‘// does so for us.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));}
19
// read homework grades // from an input stream into a `vector<double>'istream& read_hw(istream& in, vector<double>& hw){ if (in) { // get rid of previous contents hw.clear();
// read homework grades double x; while (in >> x) hw.push_back(x);
// clear the stream so that input will work for next student in.clear(); } return in;}
20
int main(){ // ask for and read the student's name cout << "Please enter your first name: "; string name; cin >> name; cout << "Hello, " << name << "!" << endl; // ask for and read the midterm and final grades cout << "Please enter your midterm and final exam grades: "; double midterm, final; cin >> midterm >> final; // ask for the homework grades cout << "Enter all your homework grades, followed by end-of-file: ";
vector<double> homework; // read the homework grades read_hw(cin, homework);
// compute and generate the final grade, if possible 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;}
프로그램 뜯어 보면
함수 호출 순서를 잘 살펴보자 . 누가 누구에게 일을 시키는지 잘 따져봐야 합니다 . main() 이 read_hw() 을 호출하여 데이터 읽어들이고 , grade() 를 호출하고 , grade() 는 인자 위치에서 median() 호출하고 이어 ( 이름은 같지만 매개변수가 다른 ) grade() 를 호출
21
try 와 catch 에 의한 오류 처리
22
int main(){ …
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, const vector<double>& hw){ if(hw.size()==0) throw domain_error(“student has done no homework”);
return grade(midterm, final, median(hw));}
<stdexcept> header exception
logic_error domain_error invalid_argument length_error out_of_range
runtime_error range_error overflow_error underflow_error
try 와 catch 에 의한 오류 처리
A 를 try( 시도 ) 하다가 오류 ( 예외 ) 가 발생하면 ,
오류 발생지점에서 예외상황 객체를 throw 한다 catch 가 그것을 받아 B 를 실행하여 오류 처리한다 . (
대부분의 경우 , A 에서 호출되는 함수 속에서 throw 함 ) 예외상황이 발생한 그 다음의 코드는 실행이 안 된다 .
오류가 발생하지 않으면 , catch 의 B 를 무시하고 , 다음 문으로 이동한다 .
23
try { A } catch (domain_error) { B }
try 와 catch 에 의한 오류 처리 - 부적절
아래와 같이 하면 어떤 문제가 숨어 있나 ?
문제점 .. << 연산자가 왼쪽에서 오른쪽 순서로 실행하지만 , 피연산자들을 그 순서대로 계산 ( 평가 ) 하지는 않는다 .
출력스트림의 정밀도를 3으로 설정한 후 , 원래 값으로 되돌리는 두번째 호출을 실행하지 못할 수도 있다 .
전적으로 구현시스템에 의존
24
try { streamsize prec=cout.precision(); cout<< “Your grade is “ <<setprecision(3) <<grade(midterm,final,homework)<<setprecision(prec); } catch (domain_error) { … }
예외 처리
예외란 우리가 당연하다고 가정한 상황이 거짓이 되는 경우를 말한다 .
대표적인 예외의 경우 컴퓨터에 사용 가능한 메모리가 부족한 경우 당연히 있을 것이라 생각한 파일이 없는 경우 사용자가 잘못된 값을 입력하는 경우
예외 처리란 이러한 상황이 발생한 경우에도 프로그램이 올바르게 동작할 수 있도록 처리하는 것을 말한다 .
예 ) 컴퓨터에 사용 가능한 메모리가 부족해서 동적 메모리 할당이 실패한 경우에 예외 처리를 잘 하지 못한 프로그램은 비정상 종료할 수 있다 . 반면에 예외 처리를 잘 한 프로그램은 사용자가 작업 중이던 정보를 잘 보관한 후에 정상적으로 종료할 수 있다 .
25
예외처리 메커니즘
26
C++ allows you to throw value of any data type, including standard library's exception classes and
your own exception classes.
throw 20;throw "Something went wrong";throw domain_error("reason comes here");
예외 처리 예
27
int main(void){ int a, b;
cout<<" 두 개의 숫자 입력 : "; cin>>a>>b;
try{ cout<<"try block start"<<endl;
if(b==0)
throw b; cout<<"a/b 의 몫 : "<<a/b<<endl; cout<<"a/b 의 나머지 : "<<a%b<<endl;
cout<<"try block end"<<endl; }
catch(int exception){ cout<<"catch block start"<<endl; cout<<exception<<" 입력 ."<<endl; cout<<" 입력오류 ! 다시 실행 하세요 ."<<endl; } cout<<"THANK YOU!"<<endl; return 0;}
예외가 발생하면 try 블록의 나머지 부분 무시
예외 처리 후 catch 블록 이후부터 실행
4.2 데이터 구조화하기
지금까지는 한 학생 성적 계산하는 프로그램 그냥 계산기로 처리하는 게 더 간편할 듯
실제에서는 여러 학생 성적 처리 필요입력Smith 93 91 47 90 92 73 100 87Carpenter 75 90 87 92 93 60 0 98…
출력 Carpenter 86.8Smith 90.4…
문제가 복잡해 졌다 : 각 학생들의 이름과 성적들을 함께 저장해야 한다
28
• 학생 이름 순서대로 출력• 점수 위치를 맞추어서 출력
데이터 구조화하기 여러 학생의 정보를 저장하기 위한 자료구조 필요
한 학생은 < 이름 , 중간 , 기말 , 과제 성적 > 이 있다 한 학생의 과제 성적을 저장할 때 , 과제성적이 모두 double 이므로
vector<double> 타입이면 OK 이제는 vector<???>
??? 에 { 이름 , 중간 , 기말 , 과제 성적 } 을 담는 무엇인가가 필요
게다가 여러 학생의 데이터를 저장하는 데에도 vector 필요
29
학생의 데이터를 모두 함께 저장하기
학생 데이터를 위한 자료구조 struct Student_info { string name; double midterm, final; vector<double> homework;};
Student_info 는 struct 타입이다 이 타입은 네 개의 데이터 멤버를 가지고 있음 타입이므로 이런 것이 된다 .
30
Student_info s; // 한 명의 학생 정보vector<Student_info> students; // 여러 명의 학생 정보
학생레코드 관리하기
한 학생의 레코드를 읽어 들이려면 ,
최종 성적을 계산하려면 ,
31
istream& read(istream& is, Student_info& s){ is>>s.name>>s.midterm>>s.final; read_hw(is, s.homework); return is;}
double grade( const Student_info& s){ return grade(s.midterm, s.final, s.homework);}
매개변수를 Student_info& s 로 한
이유는 ? const Student_info& s 로
한 이유는 ?
학생레코드 관리하기 - 이름으로 정렬
32
이름 순으로 정렬해야 하는데 , 이렇게 하면 될까 ?
sort(students.begin(), students.end()); // 안된다 . 왜 ?
// 도대체 무엇을 기준으로 정렬하란 말인가 ?
이 상황에서는 비교할 방법을 알려줘야 한다 . 이름 순으로 정렬한다면 , 우선 compare() 함수를 정의한다 .
라이브러리 함수인 sort() 의 세 번째 인자로 전달해서 호출한다 sort( students.begin(), students.end(), compare);
bool compare(const Student_info& x, const Student_info& y){ return x.name < y.name;}
보고서 양식 출력하기
몇 군데 주목해 볼만한 곳은 , 위치 맞추어 출력하기 ( 가장 긴 이름을 기준으로 )
Carpenter 86.8
Smith 90.4 오류 처리새로운 형태의 catch
33
int main(){ vector<Student_info> students; Student_info record; string::size_type maxlen = 0;
// read and store all the records, and find the length of the longest name while (read(cin, record)) { maxlen = max(maxlen, record.name.size()); students.push_back(record); }
34
// alphabetize the records sort(students.begin(), students.end(), compare);
for (vector<Student_info>::size_type i = 0; i != students.size(); ++i) { // write the name, // padded on the right to `maxlen' `+' `1‘ characters cout << students[i].name << string(maxlen + 1 - students[i].name.size(), ' '); // compute and write the grade try { double final_grade = grade(students[i]); streamsize prec = cout.precision(); cout << setprecision(3) << final_grade << setprecision(prec); } catch (domain_error e) { cout << e.what(); // call what() member function of domain_error }
cout << endl; }
return 0;
}
독립 컴파일 (separate compile)
35
프로그램이 커지면 독립 컴파일이 필수적이다 . 관련있는 함수와 자료구조를 모아 독립된 파일로 만든다
___.cpp 와 ___.h 파일 각 파일을 독립적으로 컴파일 한 후 ,
목적 파일을 링크하여 하나의 실행 파일을 만든다
예 . median() 함수를 독립된 파일로 해 보자 . 파일 이름은 median.cpp 와 median.h ( 함수 이름을 따는 것이 관례 )
중앙값 계산 소스파일 - median.cpp
36
// source file for the `median' function#include <algorithm> // to get the declaration of `sort'#include <stdexcept> // to get the declaration of `domain_error'#include <vector> // to get the declaration of `vector'
using std::domain_error; using std::sort; using std::vector;
#include "median.h”
// compute the median of a `vector<double>'double median(vector<double> vec){ …}
median() 이 필요로 하는 헤더를 모두 include
컴파일러에게 이 위치에 median.h 의 내용을 복사해서 넣어라는 의미
C++ 구현시스템이 제공하는 표준 헤더 < > 우리가 만든 헤더 ””
헤더 파일 median.h
헤더 파일에는 함수의 선언문 ( 프로토타입 ) 만 적는다 헤더 파일에는 꼭 필요한 이름만 선언한다
using 사용 자제해야 한다 . 왜 ? – 함수에서 using 을 사용하지 않을수도 있음 .
초기 버전
#ifndef 지시문을 사용해서 정의해야 한번 이상 include 되어도 안전 !! 이렇게 하지 않으면 위험한가 ? 왜 ?
최종 버전
37
#include <vector>double median(std::vector<double>);
#ifndef GUARD_median_h#define GUARD_median_h#include <vector>double median(std::vector<double>);#endif
// 함수 선언부에서 vector 를 사용하므로
// 전처리기 변수
계속해 보자 .
38
이제 무엇을 묶을까 ? 무엇이 남아 있나 ?
자료 구조 : Student_info 함수 : compare(), read(), read_hw(), 오버로드 된
grade() 들
이들 각각을 하나의 파일로 할까 ? 아니면 관련 있는 것들을 모아 하나로 할까 ?
Student_info 와 관련 함수를 묶자 Student_info compare(), read(), read_hw(),
Student_info 헤더 파일 - Student_info.h
Student_info 와 관련 함수를 묶자
39
#ifndef GUARD_Student_info#define GUARD_Student_info
// `Student_info.h' header file#include <iostream>#include <string>#include <vector>
struct Student_info {std::string name;double midterm, final;std::vector<double> homework;
};
bool compare(const Student_info&, const Student_info&);std::istream& read(std::istream&, Student_info&);std::istream& read_hw(std::istream&, std::vector<double>&);
#endif
Student_info 소스파일 - Student_info.cpp
40
// source file for `Student_info'-related functions#include "Student_info.h"
using std::istream; using std::vector;
bool compare(const Student_info& x, const Student_info& y){
return x.name < y.name;}
istream& read(istream& is, Student_info& s){
…}
istream& read_hw(istream& in, vector<double>& hw){
…}
따져 보자
이들을 하나로 묶은 것은 잘한 일인가 ? 논리적으로 관련성이 있나 ? compare() 는 어떤가 ?
.cpp 에 .h 를 include 하면 , .cpp 파일 안에 함수의 선언 (declaration) 과 정의
(definition) 를 모두 갖게 되는데 , 적절한 일인가 ?
41
이 구조체를 사용할 때에만 사용하는 함수들이므로 구조체 정의와 함께 모아 놓는 것이 좋다
중복은 문제 될 것이 없다 .사실 좋은 아이디어다 . 왜냐하면컴파일러는 선언부와 정의부가 일치하는지 체크할 수 있기 때문 . 불완전하다 .
성적 계산 프로그램 – grade.h
42
#ifndef GUARD_grade_h#define GUARD_grade_h
// `grade.h'#include <vector>#include "Student_info.h"
double grade(double, double, double);double grade(double, double, const std::vector<double>&);double grade(const Student_info&);
#endif
오버로드 된 grade() 함수를 한 눈 에 볼 수 있어서 좋다 .
왜 Student_info.h 를 include 했을까 ?
성적 계산 프로그램 – grade.cpp
43
#include <stdexcept>#include <vector>#include "grade.h"#include "median.h"#include "Student_info.h"
using std::domain_error; using std::vector;
double grade(double midterm, double final, double homework){ … }
double grade(double midterm, double final, const vector<double>& hw){ … }
double grade(const Student_info& s){ … }
이제 main() 을 완성하면 끝– main.cpp
44
#include <algorithm>#include <iomanip>#include <ios>#include <iostream>#include <stdexcept>#include <string>#include <vector>#include "grade.h"#include "Student_info.h"
int main(){ vector<Student_info> students; Student_info record; string::size_type maxlen = 0; // the length of the longest name
// read and store all the students' data. // Invariant: `students' contains all the student records // read so far, `maxlen' contains the length of the longest name in `students‘ while (read(cin, record)) { // find length of longest name maxlen = max(maxlen, record.name.size()); students.push_back(record); }
주석문 다는 방법에 주목해 보자 .
45
// alphabetize the student records sort(students.begin(), students.end(), compare);
// write the names and grades for (vector<Student_info>::size_type i = 0; i != students.size(); ++i) { // write the name, padded on the right to `maxlen' `+' `1' characters cout << students[i].name << string(maxlen + 1 - students[i].name.size(), ' ');
// compute and write the grade try { double final_grade = grade(students[i]); streamsize prec = cout.precision(); cout << setprecision(3) << final_grade << setprecision(prec); } catch (domain_error e) { cout << e.what(); } cout << endl; } return 0;}