450 likes | 688 Views
Chapter 4. 프로그램과 데이터의 구조화 ORGANIZING PROGRAMS AND DATA. 4 장에서는 …. 3.2.2 절의 프로그램이 생각했던 것보다 긴가요 ? 하지만 vector 나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다 . 표준 라이브러리 의 힘입니다 . 프로그램이 커지면 , 프로그램을 여러 독립적인 부분으로 분할하고 개별 관리 C++ 는 이런 방법을 제공 : 함수 와 자료구조
E N D
Chapter 4 프로그램과 데이터의 구조화ORGANIZING PROGRAMS AND DATA
4 장에서는… • 3.2.2절의 프로그램이 생각했던 것보다 긴가요? 하지만 • vector나 string, sort 가 없었다면 훨씬 더 길어졌을 것입니다. 표준 라이브러리의 힘입니다. • 프로그램이 커지면, • 프로그램을 여러 독립적인 부분으로 분할하고개별 관리 • C++는 이런 방법을 제공: 함수와 자료구조 • 그리고 함수와 자료구조를 하나의 이름으로 묶는 클래스 제공 (클래스 만드는 법은 9장에서 공부) • 4장은, • 함수와 자료구조 (struct)를 이용하여 프로그램의 구조화 방법 • 프로그램을 여러 파일로 나누어 독립적으로 컴파일 하는 방법
4.1 함수를 이용한 구조화하기 • 만일 같은 계산을 하는 코드가 여러 곳에 있다면… • 성적 계산 방식이 바뀌면… • 수십만 라인의 프로그램의 곳곳에 퍼져 있다면, 재앙… • 함수를 사용 • 계산을 매번 반복하지 않고, 함수를 만들고, 그 함수를 이용한다 • 변경하고 싶을 때 바꾸기가 쉽다 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로 복사된다 ret-type function-name (param-decls) { // 함수 정의 // 함수 본체 }
함수 만들기: 성적 계산 함수 매개변수 (parameter) • 성적계산 grade() 함수 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) ; … … 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에 대한 constvector<double>& chw1=chw; // 읽기전용별칭 • 참조에 의한 호출 (call-by-reference) • 함수 매개변수에레퍼런스 &를 넣으면,해당 인자를 복사하지 않으므로, 복사에 따른 간접비용 없음 • const를 넣으면 인자의 값을 바꾸지 않겠다고 구현시스템에 알려줌
call by reference • 참조에 의한 호출 예. 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: • 이 함수에서 인자의 값을 변경하지 않고, 읽기만 한다. • 어떤경우에 무엇을 쓸지 판단해야 한다. doublemedian( vector<double> vec) { …} double grade(double midterm, double final, double homework) { …} doublegrade(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(); // 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; }
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에 의한 오류 처리 try { A } catch (domain_error) { B } • A를 try(시도)하다가 • 오류(예외)가발생하면, • 오류 발생지점에서 예외상황 객체를 throw한다 • catch가 그것을 받아 B를실행하여 오류 처리한다. (대부분의 경우, A에서 호출되는 함수 속에서 throw함) • 예외상황이 발생한 그 다음의 코드는 실행이 안 된다. • 오류가 발생하지 않으면, • catch의 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 throwvalue 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");
예외 처리 예 int main(void) { int a, b; cout<<"두 개의 숫자 입력 : "; cin>>a>>b; try{ cout<<"try block start"<<endl; if(b==0) throwb; 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 • Student_info와 관련 함수를 묶자 #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) { … }
따져 보자 • 이들을 하나로 묶은 것은 잘한 일인가? • 논리적으로 관련성이 있나? • compare()는 어떤가? • .cpp에 .h를 include하면, • .cpp 파일 안에 함수의 선언 (declaration)과 정의 (definition)를 모두 갖게 되는데, 적절한 일인가? • 이 구조체를 사용할 때에만 사용하는 함수들이므로구조체 정의와 함께 모아 놓는 것이 좋다 중복은 문제 될 것이 없다. 사실 좋은 아이디어다. 왜냐하면 컴파일러는 선언부와 정의부가 일치하는지 체크할 수 있기 때문. 불완전하다.
성적 계산 프로그램 – grade.h • 오버로드 된 grade() 함수를 한 눈 에 볼 수 있어서 좋다. • 왜 Student_info.h를 include했을까? #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.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; }