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

Slides:



Advertisements
Similar presentations
12 장 템플릿 (template) Sung-Min Jung Internet Management Technology Lab. School of Information & Communication Engineering, Sungkyunkwan Univ. 300 Cheoncheon-dong,
Advertisements

YES C 제 1 장 C 언어의 개요 1/34 제 1 장 C 언어의 개요 문봉근. YES C 제 1 장 C 언어의 개요 2/34 제 1 장 C 언어의 개요 1.1 프로그램과 C 언어의 특징 1.2 C 언어의 프로그램 구성 1.3 비주얼 C++ 통합 환경 들어가기.
화일구조.
3. C++와 객체지향 C++ 코딩 방법 객체 단위로 2 개의 파일 인터페이스 파일 구현파일
Vision System Lab, Sang-Hun Han
* 07/16/96 처음으로 배우는 C 프로그래밍 제1부 기초 제1장 시작하기 *.
C++ Tutorial 1 서강대학교 데이터베이스 연구실.
RapidJson NEXT 김명찬.
명품 C++ 프로그래밍 3장. 클래스와 객체.
명품 C++ 4장. 객체 포인터와 객체 배열, 객체의 동적 생성.
Power C++ 제6장 포인터와 문자열.
C++ Espresso 제3장 배열과 포인터.
C++ Espresso 제3장 배열과 포인터.
C++ Espresso 제1장 기초 사항.
C++ Espresso 제2장 제어문과 함수.
Internet Computing KUT Youn-Hee Han
강좌명 : C++프로그래밍 (C++ Programming)
제 12장 예외 처리 12.1 설계 쟁점 12.2 PL/I의 ON-조건 12.3 Ada의 예외 처리
C++ 프로그래밍 년 2학기 전자정보공학대학 컴퓨터공학부.
제6장 객체배열과 벡터 객체 배열을 이해한다. 벡터(vector) 클래스를 사용할 수 있다.
명품 C++ 13장 예외 처리와 C 언어와의 링크 지정.
8. 객체와 클래스 (기본).
C 6장. 함수 #include <stdio.h> int main(void) { int num;
C++ 프로그래밍 년 2학기 전자정보공학대학 컴퓨터공학부.
Starting Out with C++: Early Objects 5th Edition
10장 템플릿과 표준 템플릿 라이브러리(STL)
배열, 포인터, 참조 배열은 같은 형을 가지는 변수들의 묶음이다..
윤성우의 열혈 C 프로그래밍 윤성우 저 열혈강의 C 프로그래밍 개정판 Chapter 14. 포인터와 함수에 대한 이해.
제15장 STL과 람다식 STL의 개념을 이해하고 사용할 수 있다. 람다식을 이해하고 사용할 수 있다.
윤성우의 열혈 C 프로그래밍 윤성우 저 열혈강의 C 프로그래밍 개정판 Chapter 02. 프로그램의 기본구성.
3장. 포인터, 배열, 구조체 포인터, 배열, 구조체 학습목표 기본적 데이터 타입
18장. 헤더 파일과 구현 파일 01_ 헤더 파일과 구현 파일의 사용.
14장. 함수 1 01_ 함수의 기본 02_ 인자의 전달.
C++ 프로그래밍 년 2학기 전자정보공학대학 컴퓨터공학부.
C ++ 프로그래밍 시작.
Chapter 1 C++ 기초 Copyright © 2010 Pearson Addison-Wesley. All rights reserved.
C++ Programming: chapter 7 – inheritence
C++ 개요 객체지향 윈도우즈 프로그래밍 한국성서대학교 유일선
17장. 문자열 01_ 문자열 사용의 기본 02_ 문자열의 사용.
14. 예외처리.
상속 및 동적바인딩 사용하기 USING INHERITANCE AND DYNAMIC BINDING
C++ 프로그래밍 년 2학기 전자정보공학대학 컴퓨터공학부.
제14장 예외처리와 템플릿 예외 처리의 개요를 학습한다. 예외 처리를 적용할 수 있다. 템플릿의 개념을 이해한다.
adopted from KNK C Programming : A Modern Approach
추상 데이터 타입 정의하기 Defining abstract data types
C언어 프로그래밍의 이해 Ch13. 선행처리기와 주석문.
컴퓨터의 기초 제 2강 - 변수와 자료형 , 연산자 2006년 3월 27일.
가상함수와 추상 클래스.
Chapter 1 C와는 다른 C++. 최호성.
제2장 제어구조와 배열 if-else 문에 대하여 학습한다. 중첩 if-else 문에 대하여 학습한다.
4. 고급변수 사용 : 포인터와 관련하여 메모리 바라보기
컴퓨터공학실습(I) 3주 인공지능연구실.
프로그래밍 기초와 실습 Chapter 11 Recursion.
루프와 카운트 Looping and counting
문자열 처리하기 working with Strings
Operating System Multiple Access Chatting Program using Multithread
제8장 포인터와 동적객체 생성 포인터의 개념을 이해한다. 포인터와 관련된 연산을 이해한다.
C-언어와 기반구조 정보보호학과 이정수 교수.
5. 논리적 자료표현 : 구조체.
화일구조.
누구나 즐기는 C언어 콘서트 제2장 기초 사항 IT응용시스템공학과 김형진 교수.
제 11장. 템플릿과 STL 학기 프로그래밍언어및실습 (C++).
03. 메모리 관리 C++ 프로그램에서 다룰 수 있는 메모리의 종류
C++ Espresso 제13장 입출력과 파일처리.
포인터와 배열 조 병 규 한 국 교 통 대 학 교 SQ Lab..
새로운 타입 정의하기 Defining new types
Structures Summary.
C.
C++ 언어의 특징
배열, 포인터, 함수 Review & 과제 1, 2.
Presentation transcript:

프로그램과 데이터의 구조화 Organizing programs and data Chapter 4 프로그램과 데이터의 구조화 Organizing programs and data

4 장에서는… 3.2.2절의 프로그램이 생각했던 것보다 긴가요? 하지만 프로그램이 커지면, 4장은, vector나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다. 표준 라이브러리의 힘입니다. 프로그램이 커지면, 프로그램을 여러 독립적인 부분으로 분할하고 개별 관리 C++는 이런 방법을 제공: 함수와 자료구조 그리고 함수와 자료구조를 하나의 이름으로 묶는 클래스 제공 (클래스 만드는 법은 9장에서 공부) 4장은, 함수와 자료구조 (struct)를 이용하여 프로그램의 구조화 방법 프로그램을 여러 파일로 나누어 독립적으로 컴파일 하는 방법

4.1 함수를 이용한 구조화하기 만일 같은 계산을 하는 코드가 여러 곳에 있다면… 함수를 사용 성적 계산 방식이 바뀌면… 수십만 라인의 프로그램의 곳곳에 퍼져 있다면, 재앙… 함수를 사용 계산을 매번 반복하지 않고, 함수를 만들고, 그 함수를 이용한다 변경하고 싶을 때 바꾸기가 쉽다 int main( ) { … cout …<< 0.2*midterm+0.4*final+0.4*sum/count ; return 0; }

함수의 구조 함수는 이름, 매개변수, 몸체, 그리고 리턴 타입으로 구성 ret-type: 함수가 결과로 리턴하는 값의 타입 function-name: 함수의 이름 param-decls: 함수의 매개변수. 여러 개 일 때 콤마(,)로 구분 함수는 이름, 매개변수, 몸체, 그리고 리턴 타입으로 구성 함수 호출을 할 때, 인자는 개수와 타입이 일치해야 한다 값에 의한 호출 (call-by-value): 매개변수가 지역변수로 생성되고, 인자의 값이 1:1로 복사된다 ret-type function-name (param-decls) { // 함수 정의 // 함수 본체 }

함수 만들기: 성적 계산 함수 성적계산 grade() 함수 매개변수 (parameter) 인자 (argument) double grade(double midterm, double final, double homework) { return 0.2*midterm+0.4*final+0.4*homework; } 매개변수를 지역변수로 보고, 인자 전달 과정을 지역변수 초기화로 볼 수 있음 int main( ) { … cout …<< grade(midterm, final, sum/count) ; return 0; } 인자 (argument) grade()함수 호출

함수 만들기: 중앙값 찾기 무엇을 받고 무엇을 리턴 해야 할까? : 함수 설계의 첫 단계 무엇을 받고 무엇을 리턴 해야 할까? : 함수 설계의 첫 단계 여러 값을 저장한 vector 객체를 받고, 중앙값을 리턴 // 이 함수를 호출하면 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)를 가지고, 나머지 부분을 건너뜀. 이 예외상황 객체는 호출한 곳에서 적절히 대처하는데 필요한 정보를 담고 있음

함수 만들기: 성적 계산식 다시 구현하기 또 다른 grade() 함수 몇 가지 생각해 볼 것은… 여러 과제 성적을 받아, 중앙값을 계산하고, 그것으로 최종 성적을 계산하는 기능 이전 grade()함수는 이미 계산된 중앙값 또는 평균값을 받았음 // 중간, 기말, 과제성적을 이용하여 전체 성적 계산 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) 변수 레퍼런스 또는 참조 변수는 객체에 대한 또 다른 이름 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 참조에 의한 호출 예. swap() 함수 예 void foo(int & r) { r = 10; } void main()    int x = 20;    foo(x);    cout << x; swap() 함수 예 // 옳게 작동 안 함 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; }

함수오버로딩(overloading) 오버로딩: 함수 오버로딩과 연산자 오버로딩 함수 오버로딩 여러 함수가 같은 이름을 가질 수 있다. 예. 두 개의 서로 다른 grade() 함수 double grade(double midterm, double final, double homework) { … } double grade(double midterm, double final, const vector<double>& hw) 호출되었을 때, 컴파일러는 어떤 것을 기동할지 어떻게 알지?

함수 오버로딩 예 함수 오버로딩에서는 두 함수의 매개변수의 순서, 개수 또는 타입이 달라야 한다. void foo(int) { ... }; // 1 void foo(double) { ... }; // 2 void foo(int, int) { ... }; // 3 void foo(int, double) { ... }; // 4 int foo(int); // Compiler Error!   void main() {    foo(3); // Calls 1    foo(5, 3.14); // Calls 4 }

과제 성적 읽기 읽어 들이는 기능을 함수로 만들어 보면, 함수의 골격은 아래와 같이… 리턴해야 할 것이 두 가지 읽은 값을 리턴 해야 하고, 또한 제대로 읽었는지 (오류 여부) 리턴 해야 함. 함수는 하나만 리턴 할 수 있다 레퍼런스 매개 변수를 이용하여 읽은 값을 리턴 하자! 함수의 골격은 아래와 같이… // 입력 스트림으로부터 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의 내부에 있는 오류 상태 리셋 필요 다음 학생 성적 입력 전에 정리 필요 istream& read_hw(istream& in, vector<double>& hw) { double x; while( in>>x ) hw.push_back(x); return in; }

과제 성적 읽기: 함수 구현 read_hw() 완전한 코드 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: 이 함수에서 인자의 값을 변경하지 않고, 읽기만 한다. 어떤 경우에 무엇을 쓸지 판단해야 한다. 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) { …}

함수를 이용한 성적계산 - 전체프로그램 #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]; }

// compute a student's overall grade from midterm and // final exam grades and homework grade double 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));

// 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(); double x; while (in >> x) hw.push_back(x); // clear the stream so that input will work for next student in.clear(); } return in;

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()를 호출

try와 catch에 의한 오류 처리 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; <stdexcept> header exception logic_error domain_error invalid_argument length_error out_of_range runtime_error range_error overflow_error underflow_error 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)); }

try와 catch에 의한 오류 처리 A를 try(시도)하다가 try { A } catch (domain_error) { B 오류(예외)가 발생하면, 오류 발생지점에서 예외상황 객체를 throw한다 catch가 그것을 받아 B를 실행하여 오류 처리한다. (대부분의 경우, A에서 호출되는 함수 속에서 throw함) 예외상황이 발생한 그 다음의 코드는 실행이 안 된다. 오류가 발생하지 않으면, catch의 B를 무시하고, 다음 문으로 이동한다. try { A } catch (domain_error) { B }

try와 catch에 의한 오류 처리 - 부적절 아래와 같이 하면 어떤 문제가 숨어 있나? 문제점.. << 연산자가 왼쪽에서 오른쪽 순서로 실행하지만, 피연산자들을 그 순서대로 계산(평가)하지는 않는다. 출력스트림의 정밀도를 3으로 설정한 후, 원래 값으로 되돌리는 두번째 호출을 실행하지 못할 수도 있다. 전적으로 구현시스템에 의존 try { streamsize prec=cout.precision(); cout<< “Your grade is “ <<setprecision(3) <<grade(midterm,final,homework)<<setprecision(prec); } catch (domain_error) { … }

예외 처리 예외란 우리가 당연하다고 가정한 상황이 거짓이 되는 경우를 말한다. 대표적인 예외의 경우 컴퓨터에 사용 가능한 메모리가 부족한 경우 당연히 있을 것이라 생각한 파일이 없는 경우 사용자가 잘못된 값을 입력하는 경우 예외 처리란 이러한 상황이 발생한 경우에도 프로그램이 올바르게 동작할 수 있도록 처리하는 것을 말한다. 예) 컴퓨터에 사용 가능한 메모리가 부족해서 동적 메모리 할당이 실패한 경우에 예외 처리를 잘 하지 못한 프로그램은 비정상 종료할 수 있다. 반면에 예외 처리를 잘 한 프로그램은 사용자가 작업 중이던 정보를 잘 보관한 후에 정상적으로 종료할 수 있다.

예외처리 메커니즘 C++ allows you to throw value of any data type, throw "Something went wrong"; throw domain_error("reason comes here"); C++ allows you to throw value of any data type, including standard library's exception classes and your own exception classes.

예외 처리 예 예외가 발생하면 try 블록의 나머지 부분 무시 예외 처리 후 catch 블록 이후부터 실행 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 87 Carpenter 75 90 87 92 93 60 0 98 … 출력 Carpenter 86.8 Smith 90.4 문제가 복잡해 졌다: 각 학생들의 이름과 성적들을 함께 저장해야 한다 학생 이름 순서대로 출력 점수 위치를 맞추어서 출력

데이터 구조화하기 여러 학생의 정보를 저장하기 위한 자료구조 필요 한 학생은 <이름, 중간, 기말, 과제 성적>이 있다 한 학생의 과제 성적을 저장할 때, 과제성적이 모두 double이므로 vector<double> 타입이면 OK 이제는 vector<???> ???에 {이름, 중간, 기말, 과제 성적}을 담는 무엇인가가 필요 게다가 여러 학생의 데이터를 저장하는 데에도 vector 필요

학생의 데이터를 모두 함께 저장하기 학생 데이터를 위한 자료구조 struct Student_info { string name; double midterm, final; vector<double> homework; }; Student_info는 struct 타입이다 이 타입은 네 개의 데이터 멤버를 가지고 있음 타입이므로 이런 것이 된다. Student_info s; // 한 명의 학생 정보 vector<Student_info> students; // 여러 명의 학생 정보

학생레코드 관리하기 한 학생의 레코드를 읽어 들이려면, 최종 성적을 계산하려면, istream& read(istream& is, Student_info& s) { is>>s.name>>s.midterm>>s.final; read_hw(is, s.homework); return is; } 매개변수를 Student_info& s로 한 이유는? const Student_info& s로 한 이유는? double grade( const Student_info& s) { return grade(s.midterm, s.final, s.homework); }

학생레코드 관리하기-이름으로 정렬 이름 순으로 정렬해야 하는데, 이렇게 하면 될까? 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 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); }

// 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) 프로그램이 커지면 독립 컴파일이 필수적이다. 관련있는 함수와 자료구조를 모아 독립된 파일로 만든다 ___.cpp와 ___.h 파일 각 파일을 독립적으로 컴파일 한 후, 목적 파일을 링크하여 하나의 실행 파일을 만든다 예. median() 함수를 독립된 파일로 해 보자. 파일 이름은 median.cpp와 median.h (함수 이름을 따는 것이 관례)

중앙값 계산 소스파일 - median.cpp median()이 필요로 하는 헤더를 모두 include 컴파일러에게 // 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.h의 내용을 복사해서 넣어라는 의미 C++ 구현시스템이 제공하는 표준 헤더 < > 우리가 만든 헤더 ””

헤더 파일 median.h 헤더 파일에는 함수의 선언문 (프로토타입)만 적는다 헤더 파일에는 꼭 필요한 이름만 선언한다 using 사용 자제해야 한다. 왜? – 함수에서 using을 사용하지 않을수도 있음. 초기 버전 #ifndef 지시문을 사용해서 정의해야 한번 이상 include되어도 안전!! 이렇게 하지 않으면 위험한가? 왜? 최종 버전 #include <vector> double median(std::vector<double>); // 함수 선언부에서 vector를 사용하므로 #ifndef GUARD_median_h #define GUARD_median_h #include <vector> double median(std::vector<double>); #endif // 전처리기 변수

계속해 보자. 이제 무엇을 묶을까? Student_info와 관련 함수를 묶자 무엇이 남아 있나? 함수: compare(), read(), read_hw(), 오버로드 된 grade()들 이들 각각을 하나의 파일로 할까? 아니면 관련 있는 것들을 모아 하나로 할까? Student_info와 관련 함수를 묶자 Student_info compare(), read(), read_hw(),

Student_info 헤더 파일- Student_info.h #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 // 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)

따져 보자 이들을 하나로 묶은 것은 잘한 일인가? .cpp에 .h를 include하면, 논리적으로 관련성이 있나? compare()는 어떤가? .cpp에 .h를 include하면, .cpp 파일 안에 함수의 선언 (declaration)과 정의 (definition)를 모두 갖게 되는데, 적절한 일인가? 이 구조체를 사용할 때에만 사용하는 함수들이므로 구조체 정의와 함께 모아 놓는 것이 좋다 중복은 문제 될 것이 없다. 사실 좋은 아이디어다. 왜냐하면 컴파일러는 선언부와 정의부가 일치하는지 체크할 수 있기 때문. 불완전하다.

성적 계산 프로그램 – grade.h #ifndef GUARD_grade_h #define GUARD_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 #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 #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); } 주석문 다는 방법에 주목해 보자.

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