프로그램과 데이터의 구조화 organizing programs and data

45
프프프프프 프프프프 프프프 ORGANIZING PROGRAMS AND DATA Chapter 4

Upload: curran-tate

Post on 02-Jan-2016

52 views

Category:

Documents


0 download

DESCRIPTION

Chapter 4. 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA. 4 장에서는 …. 3.2.2 절의 프로그램이 생각했던 것보다 긴가요 ? 하지만 vector 나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다 . 표준 라이브러리 의 힘입니다 . 프로그램이 커지면 , 프로그램을 여러 독립적인 부분으로 분할하고 개별 관리 C++ 는 이런 방법을 제공 : 함수 와 자료구조 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

프로그램과 데이터의 구조화ORGANIZING PROGRAMS AND DATA

Chapter 4

Page 2: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

4 장에서는…

3.2.2 절의 프로그램이 생각했던 것보다 긴가요 ? 하지만

vector 나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다 . 표준 라이브러리의 힘입니다 .

프로그램이 커지면 , 프로그램을 여러 독립적인 부분으로 분할하고 개별 관리 C++ 는 이런 방법을 제공 : 함수와 자료구조 그리고 함수와 자료구조를 하나의 이름으로 묶는 클래스 제공

( 클래스 만드는 법은 9 장에서 공부 )

4 장은 , 함수와 자료구조 (struct) 를 이용하여 프로그램의 구조화

방법 프로그램을 여러 파일로 나누어 독립적으로 컴파일 하는 방법

2

Page 3: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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;}

Page 4: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수의 구조

ret-type: 함수가 결과로 리턴하는 값의 타입 function-name: 함수의 이름 param-decls: 함수의 매개변수 . 여러 개 일 때 콤마 (,) 로

구분

함수는 이름 , 매개변수 , 몸체 , 그리고 리턴 타입으로 구성 함수 호출을 할 때 , 인자는 개수와 타입이 일치해야 한다 값에 의한 호출 (call-by-value): 매개변수가 지역변수로

생성되고 , 인자의 값이 1:1 로 복사된다

4

ret-type function-name (param-decls) { // 함수 정의// 함수 본체

}

Page 5: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수 만들기 : 성적 계산 함수

성적계산 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() 함수 호출

매개변수를 지역변수로 보고 ,

인자 전달 과정을 지역변수 초기화로 볼 수 있음

Page 6: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수 만들기 : 중앙값 찾기

무엇을 받고 무엇을 리턴 해야 할까 ? : 함수 설계의 첫 단계

여러 값을 저장한 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() 에 의해 값이 변경되므로 , 시간이 걸리더라도 복사가 유용함

Page 7: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

예외 처리 (Exception Handling)

오류가 났을 때 어떻게 대처해야 하나 ? 3.2.2 절 (p.98) 에서는 그곳에서 즉시 오류 처리했음 여기서는 예외상황 (exception) 을 throw( 발생 ) 시킴

domain_error 객체를 throw 했음 : <stdexcept> 헤더에 정의됨

그러면 median() 을 호출한 곳에서 throw 된 것을 catch 하여 적절히 오류 처리함

예외가 발생하면 , throw 위치에서 실행을 멈추고 , 예외상황 객체 (exception

object) 를 가지고 , 나머지 부분을 건너뜀 . 이 예외상황 객체는 호출한 곳에서 적절히 대처하는데 필요한

정보를 담고 있음

7

Page 8: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수 만들기 : 성적 계산식 다시 구현하기

또 다른 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)

인가 ?

Page 9: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

레퍼런스 (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 를 넣으면 인자의 값을 바꾸지 않겠다고 구현시스템에

알려줌

Page 10: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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() 함수 예

Page 11: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수오버로딩 (overloading)

오버로딩 : 함수 오버로딩과 연산자 오버로딩

함수 오버로딩 여러 함수가 같은 이름을 가질 수 있다 . 예 . 두 개의 서로 다른 grade() 함수

11

double grade(double midterm, double final, double homework){ …}

double grade(double midterm, double final, const vector<double>& hw){ …}

호출되었을 때 , 컴파일러는 어떤 것을 기동할지 어떻게 알지 ?

Page 12: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수 오버로딩 예 함수 오버로딩에서는 두 함수의 매개변수의 순서 , 개수 또는

타입이 달라야 한다 .

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}

Page 13: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

과제 성적 읽기

읽어 들이는 기능을 함수로 만들어 보면 , 리턴해야 할 것이 두 가지

읽은 값을 리턴 해야 하고 , 또한 제대로 읽었는지 ( 오류 여부 ) 리턴 해야 함 .

함수는 하나만 리턴 할 수 있다 레퍼런스 매개 변수를 이용하여 읽은 값을 리턴 하자 !

함수의 골격은 아래와 같이…

13

// 입력 스트림으로부터 vector<double> 에 과제 성적을 읽어 들임istream& read_hw( istream& in, vector<double>& hw){ // 채워야 할 곳… return in;}

int main(){ vector<double> homework; if( read_hw(cin, homework) ) { /*… */ } }

Page 14: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

과제 성적 읽기 : 함수 구현

처음 시도해 본 코드 . 아래와 같이 하면 될까 ?

문제점은 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;}

Page 15: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

과제 성적 읽기 : 함수 구현

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

Page 16: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수 매개변수 양식 세가지 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) { …}

Page 17: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

함수를 이용한 성적계산 - 전체프로그램

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];}

Page 18: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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));}

Page 19: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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;}

Page 20: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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;}

Page 21: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

프로그램 뜯어 보면

함수 호출 순서를 잘 살펴보자 . 누가 누구에게 일을 시키는지 잘 따져봐야 합니다 . main() 이 read_hw() 을 호출하여 데이터 읽어들이고 , grade() 를 호출하고 , grade() 는 인자 위치에서 median() 호출하고 이어 ( 이름은 같지만 매개변수가 다른 ) grade() 를 호출

21

Page 22: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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

Page 23: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

try 와 catch 에 의한 오류 처리

A 를 try( 시도 ) 하다가 오류 ( 예외 ) 가 발생하면 ,

오류 발생지점에서 예외상황 객체를 throw 한다 catch 가 그것을 받아 B 를 실행하여 오류 처리한다 . (

대부분의 경우 , A 에서 호출되는 함수 속에서 throw 함 ) 예외상황이 발생한 그 다음의 코드는 실행이 안 된다 .

오류가 발생하지 않으면 , catch 의 B 를 무시하고 , 다음 문으로 이동한다 .

23

try { A } catch (domain_error) { B }

Page 24: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

try 와 catch 에 의한 오류 처리 - 부적절

아래와 같이 하면 어떤 문제가 숨어 있나 ?

문제점 .. << 연산자가 왼쪽에서 오른쪽 순서로 실행하지만 , 피연산자들을 그 순서대로 계산 ( 평가 ) 하지는 않는다 .

출력스트림의 정밀도를 3으로 설정한 후 , 원래 값으로 되돌리는 두번째 호출을 실행하지 못할 수도 있다 .

전적으로 구현시스템에 의존

24

try { streamsize prec=cout.precision(); cout<< “Your grade is “ <<setprecision(3) <<grade(midterm,final,homework)<<setprecision(prec); } catch (domain_error) { … }

Page 25: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

예외 처리

예외란 우리가 당연하다고 가정한 상황이 거짓이 되는 경우를 말한다 .

대표적인 예외의 경우 컴퓨터에 사용 가능한 메모리가 부족한 경우 당연히 있을 것이라 생각한 파일이 없는 경우 사용자가 잘못된 값을 입력하는 경우

예외 처리란 이러한 상황이 발생한 경우에도 프로그램이 올바르게 동작할 수 있도록 처리하는 것을 말한다 .

예 ) 컴퓨터에 사용 가능한 메모리가 부족해서 동적 메모리 할당이 실패한 경우에 예외 처리를 잘 하지 못한 프로그램은 비정상 종료할 수 있다 . 반면에 예외 처리를 잘 한 프로그램은 사용자가 작업 중이던 정보를 잘 보관한 후에 정상적으로 종료할 수 있다 .

25

Page 26: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

예외처리 메커니즘

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");

Page 27: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

예외 처리 예

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 블록 이후부터 실행

Page 28: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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

• 학생 이름 순서대로 출력• 점수 위치를 맞추어서 출력

Page 29: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

데이터 구조화하기 여러 학생의 정보를 저장하기 위한 자료구조 필요

한 학생은 < 이름 , 중간 , 기말 , 과제 성적 > 이 있다 한 학생의 과제 성적을 저장할 때 , 과제성적이 모두 double 이므로

vector<double> 타입이면 OK 이제는 vector<???>

??? 에 { 이름 , 중간 , 기말 , 과제 성적 } 을 담는 무엇인가가 필요

게다가 여러 학생의 데이터를 저장하는 데에도 vector 필요

29

Page 30: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

학생의 데이터를 모두 함께 저장하기

학생 데이터를 위한 자료구조 struct Student_info { string name; double midterm, final; vector<double> homework;};

Student_info 는 struct 타입이다 이 타입은 네 개의 데이터 멤버를 가지고 있음 타입이므로 이런 것이 된다 .

30

Student_info s; // 한 명의 학생 정보vector<Student_info> students; // 여러 명의 학생 정보

Page 31: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

학생레코드 관리하기

한 학생의 레코드를 읽어 들이려면 ,

최종 성적을 계산하려면 ,

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 로

한 이유는 ?

Page 32: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

학생레코드 관리하기 - 이름으로 정렬

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;}

Page 33: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

보고서 양식 출력하기

몇 군데 주목해 볼만한 곳은 , 위치 맞추어 출력하기 ( 가장 긴 이름을 기준으로 )

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); }

Page 34: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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;

}

Page 35: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

독립 컴파일 (separate compile)

35

프로그램이 커지면 독립 컴파일이 필수적이다 . 관련있는 함수와 자료구조를 모아 독립된 파일로 만든다

___.cpp 와 ___.h 파일 각 파일을 독립적으로 컴파일 한 후 ,

목적 파일을 링크하여 하나의 실행 파일을 만든다

예 . median() 함수를 독립된 파일로 해 보자 . 파일 이름은 median.cpp 와 median.h ( 함수 이름을 따는 것이 관례 )

Page 36: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

중앙값 계산 소스파일 - 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++ 구현시스템이 제공하는 표준 헤더 < > 우리가 만든 헤더 ””

Page 37: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

헤더 파일 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 를 사용하므로

// 전처리기 변수

Page 38: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

계속해 보자 .

38

이제 무엇을 묶을까 ? 무엇이 남아 있나 ?

자료 구조 : Student_info 함수 : compare(), read(), read_hw(), 오버로드 된

grade() 들

이들 각각을 하나의 파일로 할까 ? 아니면 관련 있는 것들을 모아 하나로 할까 ?

Student_info 와 관련 함수를 묶자 Student_info compare(), read(), read_hw(),

Page 39: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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

Page 40: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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){

…}

Page 41: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

따져 보자

이들을 하나로 묶은 것은 잘한 일인가 ? 논리적으로 관련성이 있나 ? compare() 는 어떤가 ?

.cpp 에 .h 를 include 하면 , .cpp 파일 안에 함수의 선언 (declaration) 과 정의

(definition) 를 모두 갖게 되는데 , 적절한 일인가 ?

41

이 구조체를 사용할 때에만 사용하는 함수들이므로 구조체 정의와 함께 모아 놓는 것이 좋다

중복은 문제 될 것이 없다 .사실 좋은 아이디어다 . 왜냐하면컴파일러는 선언부와 정의부가 일치하는지 체크할 수 있기 때문 . 불완전하다 .

Page 42: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

성적 계산 프로그램 – 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 했을까 ?

Page 43: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

성적 계산 프로그램 – 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){ … }

Page 44: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

이제 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); }

주석문 다는 방법에 주목해 보자 .

Page 45: 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA

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;}