Presentation is loading. Please wait.

Presentation is loading. Please wait.

새로운 타입 정의하기 Defining new types

Similar presentations


Presentation on theme: "새로운 타입 정의하기 Defining new types"— Presentation transcript:

1 새로운 타입 정의하기 Defining new types
Chapter 9 새로운 타입 정의하기 Defining new types

2 9장에서는… C++에는 두 종류의 타입이 있다 C++ 설계 시, 기본(내장) 타입: int, double, char 등
클래스 타입: string, vector, istream 등 프로그래머가 필요에 따라 애플리케이션-고유의 타입을 정의할 수 있다. C++ 설계 시, 프로그래머들이 내장 타입을 사용하는 것처럼 타입을 쉽게 사용하고 쉽게 생성할 수 있도록 하는 데에 많은 신경을 썼다 직관적이고 거침없는 인터페이스를 갖는 타입을 생성하기 위해서는 클래스 설계 시 직접 맛보고 결정하는 듯한 현실적인 프로그래밍 언어적 기능이 필요하다

3 9.1 Student_info 되짚어 생각하기 학생들의 과목 성적을 처리하는 프로그램 문제점
하지만, 다른 프로그래머들이 사용하기에는 적합하지 않다. 문제점 1) 우리의 함수를 사용하고자 하는 프로그래머들은 특정 용례를 따라야 한다 예를 들어, Student_info 객체가 생성되면, 먼저 데이터를 그 안에 읽어들일 것이라고 가정한다 그렇게 하지 못한다면, 예상치 못한 결과를 낳게 된다. 사용자가 Student_info가 유효한 데이터를 가지고 있는지 확인하고 싶다면, 실제 데이터 멤버들을 모두 확인해 보아야만 한다 이를 위해, Student_info 클래스를 어떻게 구현했는지 낱낱이 알아야 한다.

4 2) 이 프로그램을 사용하는 사람이 한번 파일로부터 읽어들인 학생레코드는 앞으로도 변하지 않는다고 가정하고 있다
현재의 코드는 이러한 가정을 만족시켜주지 못하고 있음 3) Student_info 구조체의 인터페이스(interface)가 산만하다 관례상, Student_info 객체의 상태를 변경하는 read()함수들을 하나의 헤더파일에 넣곤 한다. 이렇게 하면, 나중에 이 코드를 사용하는 사용자에게 도움을 줄 수 있다 하지만, 이런 식의 그룹화에 대한 특별한 요구사항이 없다

5 9.2 Class 타입 클래스 타입(class type): Student_info 구조체 (4.2.1절에서 생성)
연관성 있는 데이터들을 하나의 자료구조에 묶는 메커니즘 그러한 자료구조를 하나의 개체(entity)로서 다룰 수 있다 Student_info 구조체 (4.2.1절에서 생성) 데이터 멤버들을 접근하는데 아무런 제약을 두지 않았기 때문에, 프로그래머들이 직접 데이터를 다룰 수 있다. Student_info 의 세부적인 구현을 감추고, 멤버 함수만을 사용해서 객체에 접근하도록 하고 싶다. 객체를 간편하게 다룰 연산을 제공: 클래스에 대한 인터페이스 struct Student_info { std::string name; double midterm, final; std::vector<double> homework; };

6 멤버 함수 멤버함수: 클래스 객체의 멤버인 함수
Student_info 객체를 다루기 위해서는, 프로그래머들이 사용할 수 있는 인터페이스를 정의해야 한다 레코드 하나를 읽고(read), 전체성적(grade)를 계산하는 연산을 정의 Student_info 객체는 4개의 데이터 멤버와 2개의 멤버 함수로 구성 멤버함수 호출 시, s.read(cin) 또는 s.grade() struct Student_info { std::string name; double midterm, final; std::vector<double> homework; std::istream& read(std::istream&); // 추가됨 double grade() const; // 추가됨 };

7 멤버함수 read() 차이점 istream& Student_info::read(istream& in) {
이전 버전 차이점 1) 함수이름이 Student_info::read 2) Student_info 객체를 인자로 넘기지 않음. (객체의 멤버이므로) 3) 객체의 데이터 멤버를 바로 접근할 수 있음. s.midterm이 아니라 midterm istream& Student_info::read(istream& in) { in >> name >> midterm >> final; read_hw(in, homework); return in; } istream& read(istream& is, Student_info& s) { is >> s.name >> s.midterm >> s.final; read_hw(is, s.homework); return is; }

8 매개변수에는 istream& 매개변수만 필요
Student_info::read Student_info 타입의 멤버인 read라는 이름의 함수를 나타냄 :: 표시는 스코프 연산자. 예, string::size_type 의미: string 클래스의 멤버인 size_type 매개변수에는 istream& 매개변수만 필요 Student_info& s 매개변수는 함수 호출 시 언제나 암묵적으로 사용되기 때문. 함수호출시: s.read(cin) 예를 들어, string s; s.size(); s객체의 size() 멤버함수를 호출 s라는 Student_info객체에 대해 s.read(cin) 호출하면, 현재 s 객체에 대해 작업하고 있음을 의미. 따라서, midterm, final, homework는 s.midterm, s.final, s.homework 이다.

9 멤버함수 grade() 차이점 double Student_info::grade() const {
return ::grade(midterm, final, homework); } double grade(const Student_info& s) { return grade(s.midterm, s.final, s.homework); } 이전 버전 차이점 1) const인 멤버 함수: 함수 자체를 const 멤버함수로 만들어서, 객체의 내부상태를 변경하지 않는다는 것을 보장한다. 2) ::grade 함수 호출: ::를 이름 앞에 사용하면, 어느 것의 멤버도 아닌 이름을 사용한다는 의미. 호출하려는 함수가 grade(double midterm, double final, const vector<double>& hw) 임. ::grade를 사용하지 않으면, 컴파일러는 Student_info::grade로 인식함.

10 const인 멤버 함수 이 함수는 객체 안의 값들이 변하지 않음을 보장한다. 호출 가능 함수:
const인 객체에 대해서 const인 함수를 호출 할 수 있다. const인 객체에 대해서는 const가 아닌 함수들을 호출할 수 없다. 예를 들어, const Student_info객체에 대해서는 read함수를 호출할 수 없다. 객체의 값을 바꾸기 때문에.

11 비멤버 함수들 멤버함수와 비멤버함수로 나누는 일반적인 규칙
함수가 객체의 상태를 변경한다면, 객체의 멤버로 정의해야 함 객체의 상태를 변경하지 않는 함수에 대해서는 … 마땅한 규칙이 없다… compare 함수를 멤버함수로 할지? 클래스 본체 밖에 정의하는 것이 좋을지? 12.2절에서 compare함수는 클래스 본체 밖에서 정의하는 이점에 대해서 살펴볼 것이다. 여기서는 전역함수로 정의한다.

12 9.3 보호정책(Protection) C++는 데이터 은닉 기능을 제공
grade와 read함수를 멤버로 정의하면, 문제의 절반은 해결한 셈이다. Student_info의 사용자는 이제 더 이상 객체의 내부 상태를 직접 건드릴 필요가 없게 되었다. 하지만, 여전히 가능은 하다. 이제… 데이터를 감추고 싶다. 그래서, 사용자들이 멤버함수를 통해서만 접근할 수 있도록 하겠다.. C++는 데이터 은닉 기능을 제공 타입을 public으로 하면, 모든 사용자들이 접근할 수 있다 private으로 하면, 다른 사용자들은 그 타입을 접근할 수 없다. 보호레이블(Protection label) : public, private 접근 권한을 정의 레이블은 클래스 안에서 순서 상관없이 나올 수 있다.

13 Student_info 클래스 private 레이블 다음의 멤버들은 public 멤버들은 class Student_info {
// 인터페이스는 여기에 double grade() const; std::istream& read(std::istream&); private: std::string name; double midterm, final; std::vector<double> homework; }; private 레이블 다음의 멤버들은 클래스 내부의 멤버함수만이 접근 할 수 있다 비멤버함수에서 이러한 멤버들을 참조할 수 없다 public 멤버들은 클래스 내부, 외부에서의 접근을 허용한다 누구라도 read나 grade호출 가능하다

14 접근 예 main함수에서, Student_info s;
cin >> s.midterm; // 컴파일 오류: private 멤버 접근 s.read(cin); // OK cout << s.grade(); // OK

15 class와 struct 의 차이점 기본 보호모드가 다르다. = =
class를 사용하면, 처음 {와 첫번째 보호레이블 사이의 모든 멤버는 private이다. struct를 사용하면, ” 모든 멤버는 public이다. 다른 차이점은 없다: 일반적으로, 간단한 타입일 경우에는 struct를 사용 class Student_info { public: double grade() const; // … }; struct Student_info { double grade() const; // … }; = class Student_info { std::string name; // … public: double grade() const; }; struct Student_info { private: std::string name; // … public: double grade() const; }; =

16 접근함수 name() 추가 이제 데이터를 감추었기 때문에, 더 이상 Student_info 객체의 데이터를 변경할 수 없다.
read 함수를 사용하여, 데이터 멤버들을 설정하고, grade 함수를 사용하여, 최종성적을 계산한다. 여기에, 또 추가해야 할 연산은 학생의 이름을 얻을 수 있도록 함. (긴 이름에 맞춰 출력) name() 함수 추가: 이름을 리턴한다. 읽기권한만 필요함. 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; };

17 접근 함수(Accessor function)
접근 함수: 감춰진 데이터를 접근하는 함수 접근함수들은 클래스 추상 인터페이스의 일부로서만 사용되어야 한다. name() 함수: 학생의 이름을 추상화하기 위해서 제공 compare 함수 이전 버전 학생들의 이름을 얻는 방법이 다르다. bool compare(const Student_info& x, const Student_info& y) { return x.name() < y.name(); } bool compare(const Student_info& x, const Student_info& y) { return x.name < y.name; }

18 homework이 비어있는지 테스트 이제 남은 문제는 사용자가 여전히 객체의 데이터를 직접 들여다보고 싶은 경우를 처리.
이제까지 멤버를 감추고, 적당한 접근함수를 제공했다.. 이제 남은 문제는 사용자가 여전히 객체의 데이터를 직접 들여다보고 싶은 경우를 처리. 예를 들어, 어떤 객체 s에 대해 grade를 호출하는데, 그 객체는 아직 read를 통해 읽어들이지 않은 객체라면, 어떤 일이 일어날까요? 추상적인 방법을 통해, 비어있는지 테스트 valid()함수는 객체가 유효한 데이터를 가졌는지 알려준다. Student_info s; cout << s.grade() << endl; // 예외발생: s에는 데이터가 없다 class Student_info { public: bool valid() const { return !homework.empty(); } // 이전과 같음

19 9.4 Student_info 클래스 class Student_info { public:
std::string name() const { return n; } bool valid() const { return !homework.empty(); } // 9.2.1절에 정의된 그대로, name대신 n을 읽어들이도록 수정됨 std::istream& read(std::istream&); double grade() const; // 9.2.1절에 정의된 그대로 private: std::string n; double midterm, final; std::vector<double> homework; }; bool compare(const Student_info&, const Student_info&);

20 9.5 생성자(Constructor) 객체가 생성될 때, 어떤 일이 일어나는지..
라이브러리는 라이브러리 클래스 객체를 생성할 때, 객체에 적당한 값이 들어있다는 것을 가정한다. 예를 들어, string이나 vector를 초기값을 주지 않고 정의하면, 빈 string이나 빈 벡터가 생성된다. 또한, string이나 vector는 사이즈를 명시하거나 채울 문자를 지정하는 식으로 초기값을 부여한 새로운 객체를 생성할 수도 있다. 생성자는 객체가 어떻게 초기화되는지를 정의하는 특별한 멤버 함수이다. 생성자를 명시적으로 호출할 수 있는 방법은 없다 대신, 클래스 타입의 객체를 생성하면, 적절한 생성자가 자동적으로 호출된다. 생성자를 정의하지 않으면, 컴파일러가 자동으로 하나 만든다.

21 생성자의 초기화 규칙 Student_info 클래스는 3)번째 경우에 해당됨
1) 하나이상의 생성자를 정의한 클래스타입의 객체라면, 적당한 생성자가 선택되어 그 클래스의 객체들을 모두 초기화한다 2) 만약 내장 타입의 객체라면, 값지정-초기화 시에는 0으로 설정되고, 디폴트-초기화 시에는 정의되지 않은 값으로 설정된다 3) 그 외의 경우는, 생성자를 전혀 갖지 않는 객체의 경우 그 객체를 값지정-초기화 또는 디폴트-초기화 한다면, 그 데이터 멤버 각각에 대해서도 값지정, 또는 디폴트-초기화 한다. Student_info 클래스는 3)번째 경우에 해당됨 Student_info 를 지역변수로 정의하면, n과 homework 멤버는 자동으로 빈 string, 빈 vector로 초기화됨 이들은 생성자를 갖는 스트링 클래스, 벡터 클래스의 객체들이므로. midterm, final은 디폴트-초기화되어 “정의되지 않은 값(쓰레기 값)”을 갖는다.

22 Student_info 생성자 두 개의 생성자를 만들어 봅시다. 생성자를 추가한 클래스 인자를 갖지 않는 생성자
Student_info s; // 빈 Student_info 객체 입력스트림에 대한 레퍼런스를 취하고, 스트림으로부터 읽어들여 객체를 초기화 하는 생성자 Student_info s2(cin); // cin으로부터 읽어들여 s2 초기화 생성자를 추가한 클래스 class Student_info { public: Student_info(); // 빈 Student_info 객체 생성 Student_info(std::istream&); // 스트림을 읽어서 생성 // 이전과 같음 };

23 기본 생성자 (Default constructor)
아무 인자도 갖지 않는 생성자를 기본생성자라고 한다 보통 객체의 데이터 멤버들을 적당히 초기화시키기만 하면 됨 Student_info 객체의 경우, n멤버를 빈 스트링으로, homework 멤버를 빈 벡터로, midterm과 final을 0으로 초기화 시키는 예. Student_info::Student_info() : midterm(0), final(0) { } n과 homework은 암묵적으로 초기화 됨 midterm과 final은 명시적으로 초기화 해야 됨 생성자 초기 설정자(constructor initializer) :와 { 사이에서 멤버들을 초기화

24 객체가 생성되는 과정 새로운 클래스의 객체가 생성될 때의 순서
1) 구현시스템은 객체를 담을 메모리를 할당한다 2) 생성자의 초기 설정자 목록에 의해 객체를 초기화한다 3) 생성자 본체를 실행한다 생성자는 객체들이 생성될 때 제대로 된 값을 가질 수 있도록 하기 위해 존재한다 이러한 생성자의 설계의 목적에 맞도록, 모든 데이터 멤버들을 초기화 하자!

25 인자를 사용하는 생성자 Student_info::Student_info(istream& is){ read(is); }
명시적인 초기설정자를 갖고 있지 않으므로, n과 homework은 스트링과 벡터의 기본 생성자로 초기화 되고, midterm과 final은 정의되지 않은 값을 갖지만, read 함수를 통해 즉시 새로운 값으로 부여하기 때문에 문제가 되지 않는다.

26 Student_info 클래스 사용하기 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 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;


Download ppt "새로운 타입 정의하기 Defining new types"

Similar presentations


Ads by Google