| AcceleratedC++/Chapter8 | AcceleratedC++/Chapter10 |
| 기본 타입 | char, int, double 등 기본언어의 일부 |
| 클래스 타입 | string, vector, istream 등 기본언어를 가지고 구현된 타입 |
~cpp
struct Student_info {
std::string name;
double midterm, final;
std::vector<double> homework;
};
~cpp
struct Student_info {
std::string name;
double midterm, final;
std::vector<double> homework;
std::istream& read(std::istream&); //입력 스트림으로 부터 입력을 받아서 4개의 멤버변수를 초기화한다.
double grade() const; //내부 멤버변수를 활용하여 점수를 계산하고 double 형으로 리턴한다.
//함수 명의 뒤에 const를 붙이면 멤버 변수의 변형을 할 수 없는 함수가 된다. (구조적으로 좋다)
};
~cpp
istream & Student_info::read(istream& in)
{
in>>name>>midterm>>final;
read_hw(in, homework);
return in;
}
이 함수에서 알아야할 3가지
~cpp
double Student_info::grade() const {
return ::grade(midterm, final, homework);
}
::를 사용함으로써 호출되는 grade를 객체의 멤버함수가 아니라 전역 grade의 형으로 사용하는 것이 가능하다.
~cpp
class Student_info {
public:
//interface is here
double grade() const;
std::istream& read(std::istream&);
private:
//implementation is here
std::string name;
double midterm, final;
std::vector<double> homework;
};
struct 키워드 대신 class 키워드 사용. 보호레이블(protection label) 사용. 레이블은 순서없이 여러분 중복으로 나와도 무관함.
| class 키워드를 사용한 클래스 | 기본 보호모드가 private 으로 동작한다. |
| struct 키워드를 사용한 클래스 | 기본 보호모드가 public 으로 동작한다. |
~cpp
class Student_info {
public:
double grade() const;
};
struct Student_info {
dobule grade() const;
};
~cpp
class Student_info {
std::string name;
public:
double grade() const;
};
struct Student_info {
private:
std::string name;
public:
dobule grade() const;
};
일반적으로 자료구조가 간단할 때에는 struct를 이용한다. 그러나 2가지 키워드의 사용의 차이는 존재하지 않는다. 단지 문서화를 어떻게 하느냐에 의해 차이가 생길 뿐이다.
~cpp
class Student_info {
public:
double grade() const;
std::istream& read(std::istream&);
std::string name() const {return n;}
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
name 멤버 변수에 직접적으로 접근하는 대신에 name()함수를 만들어서 접근하도록 만들었다. const 함수이므로 멤버변수의 변경을 불허한다. 리턴값이 복사된 것이기 때문에 변경을 하더라도 멤버변수에 영향을 주지는 않는다.
~cpp
bool compare(const Student_info& x, const Student_info& y) {
return x.name() < y.name();
}
인자로 받은 변수들을 직접 접근하지 않고 그 멤버함수로 접근한다.
~cpp
class Student_info {
public:
double grade() const;
std::istream& read(std::istream&);
std::string name() const {return n;}
bool valid() const { return !homework.empty(); } // 사용자에게 그 객체가 유효한 데이터를 가졌는지를 알려준다.
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
상기와 같은 방식으로 통해서 grade를 호출하기 전에 객체의 유효성을 검사한다면 에러를 없애는 것이 가능하다.
~cpp
class Student_info {
public:
double grade() const;
std::istream& read(std::istream&);
std::string name() const {return n;}
bool valid() const { return !homework.empty(); } // 사용자에게 그 객체가 유효한 데이터를 가졌는지를 알려준다.
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
bool compare(const Student_info& x, const Student& y);
동일한 일을 하지만 프로그래머는 실제로 객체의 내부의 구현을 감추는 것이 가능하다.
~cpp
class Student_info {
public:
Student_info();
Student_info(std::istream&);
};
~cpp
Student_info::Student_info():midterm(0). final(0) {} // n, homework 의 경우 STL에서 제공되는 디폴트 생성자가 재귀적으로 호출
| 객체의 생성 순서 |
| * 구현 시스템이 객체를 담을만한 메모리를 할당함. * 초기 설정자 목록에 의해 객체들을 초기화 * 생성자 본체를 실행 |
~cpp
Student_info::Student_info(istream& is) {read(is);}
이 생성자는 실질적인 작업을 read 함수에 위임합니다.
~cpp
//Student_info.h
#ifndef GUARD_Student_info
#define GUARD_Student_info
#include <string>
#include <vector>
class Student_info {
public:
Student_info(); // construct an empty `Student_info' object
Student_info(std::istream&); // construct one by reading a stream
std::string name() const { return n; }
bool valid() const { return !homework.empty(); }
// as defined in 9.2.1/157, and changed to read into `n' instead of `name'
std::istream& read(std::istream&);
double grade() const; // as defined in 9.2.1/158
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
bool compare(const Student_info&, const Student_info&);
#endif
~cpp
//Student_info.cpp
#include <iostream>
#include <vector>
#include "grade.h"
#include "Student_info.h"
using std::istream;
using std::vector;
double Student_info::grade() const
{
return ::grade(midterm, final, homework);
}
bool compare(const Student_info& x, const Student_info& y)
{
return x.name() < y.name();
}
Student_info::Student_info(): midterm(0), final(0) { }
Student_info::Student_info(istream& is) { read(is); }
// 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 the next student
in.clear();
}
return in;
}
istream& Student_info::read(istream& in)
{
in >> n >> midterm >> final;
read_hw(in, homework);
return in;
}
~cpp
//grading_prog.cpp
#include <algorithm>
#include <iomanip>
#include <ios>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
using std::max;
#include "Student_info.h"
#include "median.h"
using std::cin; using std::cout;
using std::domain_error; using std::endl;
using std::setprecision; using std::setw;
using std::sort; using std::streamsize;
using std::string; using std::vector;
int main()
{
vector<Student_info> students;
Student_info record;
string::size_type maxlen = 0;
// read and store the data
while (record.read(cin)) { // changed
maxlen = max(maxlen, record.name().size()); // changed
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) {
cout << students[i].name() // this and the next line changed
<< string(maxlen + 1 - students[i].name().size(), ' ');
try {
double final_grade = students[i].grade(); // changed
streamsize prec = cout.precision();
cout << setprecision(3) << final_grade
<< setprecision(prec) << endl;
} catch (domain_error e) {
cout << e.what() << endl;
}
}
return 0;
}