| AcceleratedC++/Chapter10 | AcceleratedC++/Chapter12 |
1. Chapter 11 Defining abstract data types ¶
3장�서 작성한 Student_info 타입� 복사, 대입, 소멸시� 어떤 �� 수행�는지 명세�어있지 않�.
ì‹¤ì œë¡œ í�´ëž˜ìŠ¤ë¥¼ ì œìž‘í• ê²½ìš°ì—�는 í�´ëž˜ìФì�˜ ìƒ�성, 복사, 대입, 소멸ì�˜ ê³ ë ¤í•˜ì—¬ 설계ë�˜ì–´ì•¼í•œë‹¤.
ì�´ìž¥ì—�서는 STL vectorí�´ëž˜ìФì�˜ 약ì‹� ë²„ì „ì�„ 만들어 보면서 ì�¼ë°˜ì �ì�¸ 형태ì�˜ ìž�료형ì�„ 만드는 ë°©ì‹�ì�„ ì�µížŒë‹¤.
ì‹¤ì œë¡œ í�´ëž˜ìŠ¤ë¥¼ ì œìž‘í• ê²½ìš°ì—�는 í�´ëž˜ìФì�˜ ìƒ�성, 복사, 대입, 소멸ì�˜ ê³ ë ¤í•˜ì—¬ 설계ë�˜ì–´ì•¼í•œë‹¤.
ì�´ìž¥ì—�서는 STL vectorí�´ëž˜ìФì�˜ 약ì‹� ë²„ì „ì�„ 만들어 보면서 ì�¼ë°˜ì �ì�¸ 형태ì�˜ ìž�료형ì�„ 만드는 ë°©ì‹�ì�„ ì�µížŒë‹¤.
1.1. 11.1 The Vec class ¶
í�´ëž˜ìŠ¤ë¥¼ ì„¤ê³„í• ë•Œì—�는 ìš°ì„ ì�¸í„°íŽ˜ì�´ìŠ¤ë¥¼ ê²°ì •í•´ì•¼í•œë‹¤. ì�¸í„°íŽ˜ì�´ìФì�˜ ê²°ì •ì—�는 ì‹¤ì œë¡œ ê·¸ ê°�체를 ì�´ìš©í•œ 프로그램ì�„ 작성해보는 것ì�´ 좋다.
~cpp //vector ìƒ�성 vector<Student_info> vs; vector<double> v(100); //vectorê°€ 사용하는 타입ì�˜ ì�´ë¥´ì�„ 얻는다. vector<Student_info>::const_iterator b, e; vector<Student_info>::size_type i = 0; //vectorì�˜ ê°�요소를 살펴보기 위해, size ë°� index ì—°ì‚°ìž�를 사용 for(i = 0; i != vs.size(); ++1) cout<<vs[i].name(); //첫번재 요소와 마지막 요소ì—� 대한 반복ìž� 리턴 b=vs.begin(); e=vs.end();ì�´ìƒ�ì�˜ 것들ì�´ ì�´ìž¥ì—�서 êµ¬í˜„í• vector ì�˜ clone ë²„ì „ì�¸ Vec í�´ëž˜ìŠ¤ë¥¼ êµ¬í˜„í• ë©”ì†Œë“œë“¤ì�´ë‹¤.
1.2.1. 11.2.1 메모리 í• ë‹¹ ¶
ì—°ì‚°ì�„ ê²°ì •í•œí›„ì—�는 ì–´ë–¤ ë°©ì‹�으로 Vec를 êµ¬í˜„í• ì§€ë¥¼ ê²°ì •í•˜ëŠ” 것ì�´ë‹¤.
여기서는 template class를 �용한다.
템플릿� 함수�만 아니� �래스�서� 사용하는 것� 가능하다.
size는 begin, end 를 통해서 ê·¸ í�¬ê¸°ë¥¼ 구하는 것ì�´ 가능하므로 여기서는 첫 요소ì�˜ 주소, 마지막 요소를 하나 지난 주소를 ì €ìž¥í•˜ê³ ê°œìˆ˜ëŠ” 계산ì�„ 통해서 구현한다.
ë”°ë�¼ì„œ ì–´ë–¤ 타입ì�´ Vecì—�서 사용ë�˜ëŠ”ì§„ëŠ” ì •ì�˜ë¶€ê°€ instiation ë�˜ê¸° ì „ì—�는 알 수 없다.
여기서는 template class를 �용한다.
템플릿� 함수�만 아니� �래스�서� 사용하는 것� 가능하다.
~cpp
template <class T> class Vec {
public:
//interface
private:
//implementation
};
begin, end, size 함수를 구현해야 하므로 ì�´ëŸ¬í•œ 작업ì�„ 위해서 첫 요소ì�˜ 주소, 마지막 요소를 하나 지난 주소, 요소들ì�˜ 개수를 ì €ìž¥í• ìˆ˜ 있어야한다.size는 begin, end 를 통해서 ê·¸ í�¬ê¸°ë¥¼ 구하는 것ì�´ 가능하므로 여기서는 첫 요소ì�˜ 주소, 마지막 요소를 하나 지난 주소를 ì €ìž¥í•˜ê³ ê°œìˆ˜ëŠ” 계산ì�„ 통해서 구현한다.
~cpp
template <class T> class Vec {
public:
//interface
private:
T* data; // 첫번째 요소
T* limit; // 마지막 요소�서 하나를 지난 곳� ��터
};
템플릿ì�€ 단지 í‹€ì�¼ ë¿�ì�´ë©°, ì‚¬ìš©í• ë•Œ type parameter로 준 형ì—� ë”°ë�¼ì„œ ì‹¤ì œì�˜ í�´ëž˜ìŠ¤ë¥¼ ìƒ�성한다.ë”°ë�¼ì„œ ì–´ë–¤ 타입ì�´ Vecì—�서 사용ë�˜ëŠ”ì§„ëŠ” ì •ì�˜ë¶€ê°€ instiation ë�˜ê¸° ì „ì—�는 알 수 없다.
1.2.2. 11.2.2 ìƒ�성ìž�(Constructor) ¶
앞� �터페�스 명세�서 아래� 2가지를 가능하게 하�� 했기 때문� �성� 2개는 최소한 구현해야한다.
ì�¸ìž�ì�˜ 기본형ì�„ ì§€ì •í•´ì£¼ë©´ 한개ì�˜ í•¨ìˆ˜ì •ì�˜ë¡œë�„ 2가지ì�˜ ìƒ�성ìž�ì�˜ ì—í• ì�„ í• ìˆ˜ 있기 때문ì—� 편리하다.
explicit 키워드
ìƒ�성ìž�ê°€ 하나ì�˜ ì�¸ìž�를 받는 경우. ì�¼ë°˜ì �ì�¸ 대입 ìƒ�성ìž�와 혼용ì�´ ë� 가능성ì�´ 존재하기 때문ì—� 명시ì �ì�¸ ìƒ�성만ì�„ 허용시키는 방법ì�´ë‹¤. (12.2 ì ˆì—�서 ìž�세한 ë…¼ì�˜)
~cpp Vec<Student_info> vs; // default constructor Vec<Student_info> vs(100); // Vecì�˜ 요소ì�˜ í�¬ê¸°ë¥¼ 취하는 ìƒ�성ìž� // 표준 vector í�´ëž˜ìŠ¤ëŠ” í�¬ê¸°ì™€ 함께 초기화 요소를 ì�¸ìž�로 받는 ìƒ�성ìž�ë�„ ì œê³µí•œë‹¤.
~cpp
template <class T> class Vec {
public:
Vec() { create(); } // ì•„ì§�ìœ¼ë¡œì„ ëŠ� 구현부분ì—�서 ì •í•´ì§„ 것ì�´ 없기 때문ì—� 임시ì �으로 함수를 만들어서 넣었다.
explicit Vec(size_type n, const T& val = T()) { create(n, val); }
private:
T* data; // 첫번째 요소
T* limit; // 마지막 요소�서 하나를 지난 곳� ��터
};
2번째 ìƒ�성ìž�는 초기화ì�˜ ì�¸ìž�로 ì�¸ìž� 파ë�¼ë©”í„°ì�¸ Tì�˜ ìƒ�성ìž�를 ì�´ìš©í•œë‹¤.ì�¸ìž�ì�˜ 기본형ì�„ ì§€ì •í•´ì£¼ë©´ 한개ì�˜ í•¨ìˆ˜ì •ì�˜ë¡œë�„ 2가지ì�˜ ìƒ�성ìž�ì�˜ ì—í• ì�„ í• ìˆ˜ 있기 때문ì—� 편리하다.
explicit 키워드
ìƒ�성ìž�ê°€ 하나ì�˜ ì�¸ìž�를 받는 경우. ì�¼ë°˜ì �ì�¸ 대입 ìƒ�성ìž�와 혼용ì�´ ë� 가능성ì�´ 존재하기 때문ì—� 명시ì �ì�¸ ìƒ�성만ì�„ 허용시키는 방법ì�´ë‹¤. (12.2 ì ˆì—�서 ìž�세한 ë…¼ì�˜)
~cpp Vec<int> vi(100); // Working. int 를 통해 명시ì �ì�¸ ìƒ�성 Vec<int> vi = 100; // not working. 암묵ì �으로 Vec를 ìƒ�ì„±í•˜ê³ ê·¸ ê°’ì�„ vi ì—� 복사한다. refer 11.3.3
1.2.3. 11.2.3 타입 ì •ì�˜ ¶
const_iterator, iterator를 ì •ì�˜í•´ì•¼í•¨.
back_inserter(T)함수를 통해서 ë�™ì �으로 í�¬ê¸°ë¥¼ 변경시키기 위해서 value_type, push_back 타입ì�„ ì •ì�˜í•¨. (value_type ì�€ 현재 ì €ìž¥ë�œ 요소가 ì–´ë–¤ 타입ì�¸ì§€ë¥¼ ì•Œë ¤ì¤Œ)
listì�˜ 반복ìž�를 구현하는 경우ë�¼ë©´ ++ ì—°ì‚°ì�„ 통해서 노드로 ì—°ê²°ë�œ 다ì�Œ 노드를 리턴하는 함수를 오버로딩해야하나, 여기서는 ë°°ì—´ì�˜ 요소를 가리키므로 단순ì�´ í�¬ì�¸í„°ë¥¼ 리턴하는 것 만으로 우리는 ìž„ì�˜ ì ‘ê·¼ 반복ìž�를 구현하는 것ì�´ 가능하다.
value_typeì�€ Tê°€ ë�˜ì–´ì•¼í•˜ëŠ” 것ì�€ 당연하다. ë°°ì—´ì�˜ 개수를 표현하는 size_typeì�€ cstddef ì—� 존재하는 size_t로서 ì •ì�˜í•˜ëŠ” 것ì�´ 합당하다.
back_inserter(T)함수를 통해서 ë�™ì �으로 í�¬ê¸°ë¥¼ 변경시키기 위해서 value_type, push_back 타입ì�„ ì •ì�˜í•¨. (value_type ì�€ 현재 ì €ìž¥ë�œ 요소가 ì–´ë–¤ 타입ì�¸ì§€ë¥¼ ì•Œë ¤ì¤Œ)
listì�˜ 반복ìž�를 구현하는 경우ë�¼ë©´ ++ ì—°ì‚°ì�„ 통해서 노드로 ì—°ê²°ë�œ 다ì�Œ 노드를 리턴하는 함수를 오버로딩해야하나, 여기서는 ë°°ì—´ì�˜ 요소를 가리키므로 단순ì�´ í�¬ì�¸í„°ë¥¼ 리턴하는 것 만으로 우리는 ìž„ì�˜ ì ‘ê·¼ 반복ìž�를 구현하는 것ì�´ 가능하다.
value_typeì�€ Tê°€ ë�˜ì–´ì•¼í•˜ëŠ” 것ì�€ 당연하다. ë°°ì—´ì�˜ 개수를 표현하는 size_typeì�€ cstddef ì—� 존재하는 size_t로서 ì •ì�˜í•˜ëŠ” 것ì�´ 합당하다.
~cpp
template <class T> class Vec {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec() { create(); } // ì•„ì§�ìœ¼ë¡œì„ ëŠ� 구현부분ì—�서 ì •í•´ì§„ 것ì�´ 없기 때문ì—� 임시ì �으로 함수를 만들어서 넣었다.
explicit Vect(size_type n, const T& val = T()) { create(n, val); }
private:
iterator data; // 첫번째 요소
iterator limit; // 마지막 요소�서 하나를 지난 곳� ��터
};
ì ˆì �한 typedef를 ì¶”ê°€í•˜ê³ , 멤버 ë�°ì�´í„°ì�˜ 형ë�„ 여기ì—� 맞추어서 변경하였ì�Œ.1.2.4. 11.2.4 ì�¸ë�±ìФ ë°� size ¶
~cpp for (i = 0; i != vs.size(); ++) cout<<vs[i].name();ìƒ�기ì�˜ ì—°ì‚°ì�„ 가능하게 하기 위해서는 operator[], size() 함수를 ì •ì�˜ 해야한다.
연산� 오버로딩(operator overload)
오버로드 ì—°ì‚°ìž�ì�˜ 명ì¹ì�€ operatorop ì�˜ 형태로 나타난다. 즉
~cpp []� 경우�는 ~cpp operator[]로 나타낼 수 있다.만약 오버로드 연산�가 멤버함수가 아니�면(friend 함수) 좌측 피연산�는 첫번째 �수, 우측 피연산�는 �번재 �수로 나타낼 수 있다.
만약 멤버 함수로 ì—°ì‚°ìž�ê°€ ì •ì�˜ ë�˜ì–´ 있다면 좌측 피연산ìž�는 함수를 호출한 ê°�체로 간주ë�˜ê³ 오버로드 ì—°ì‚°ìž�는 ì�¸ìž�로 우측ì�˜ 피연산ìž�ë§Œì�„ ì�¸ìž�로 취한다.
~cpp []ì—°ì‚°ì�€ ì�¸ìž�로 size_typeì�„ ì·¨í•˜ê³ , 리턴값으로 value_typeì�˜ ë ˆí�¼ëŸ°ìŠ¤ë¥¼ ì·¨í•´ì•¼í• ê²ƒì�´ë‹¤.~cpp
template <class T> class Vec {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec() { create(); } // ì•„ì§�ìœ¼ë¡œì„ ëŠ� 구현부분ì—�서 ì •í•´ì§„ 것ì�´ 없기 때문ì—� 임시ì �으로 함수를 만들어서 넣었다.
explicit Vect(size_type n, const T& val = T()) { create(n, val); }
size_type size() const { return limit - data; }
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }; // ì�´ê²½ìš°ì—�ë�„ ë ˆí�¼ëŸ°ìŠ¤ë¥¼ 쓰는 ì�´ìœ 는 성능ìƒ�ì�˜ ì�´ìœ 때문ì�´ë‹¤.
private:
iterator data; // 첫번째 요소
iterator limit; // 마지막 요소�서 하나를 지난 곳� ��터
};
operator[] ì�˜ 2가지 ë²„ì „ì�„ 오버로딩 í• ìˆ˜ 잇는 ê·¼ê±°ëª¨ë“ ë©¤ë²„í•¨ìˆ˜ëŠ” 암묵ì �으로 한가지ì�˜ ì�¸ìž�를 ë�” 받는다. 그것ì�€ ê·¸ 함수를 호출한 ê°�ì²´ì�¸ë�°, ì�´ê²½ìš° ê·¸ ê°�ì²´ê°€ constì�´ê±°ë‚˜ const ê°€ 아닌 ë²„ì „ 2가지가 존재하는 것ì�´ 가능하기 때문ì—� parameter specification ì�´ ë�™ì�¼í•˜ì§€ë§Œ 오버로딩ì�´ 가능하다.
1.2.5. 11.2.5 반복ìž�를 리턴하는 ì—°ì‚° ¶
~cpp
template <class T> class Vec {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec() { create(); } // ì•„ì§�ìœ¼ë¡œì„ ëŠ� 구현부분ì—�서 ì •í•´ì§„ 것ì�´ 없기 때문ì—� 임시ì �으로 함수를 만들어서 넣었다.
explicit Vect(size_type n, const T& val = T()) { create(n, val); }
size_type size() const { return limit - data; }
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }; // ì�´ê²½ìš°ì—�ë�„ ë ˆí�¼ëŸ°ìŠ¤ë¥¼ 쓰는 ì�´ìœ 는 성능ìƒ�ì�˜ ì�´ìœ 때문ì�´ë‹¤.
//반복�들� 리턴하는 새로운 함수들
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return limit; }
const_iterator end() const { return limit; }
private:
iterator data; // 첫번째 요소
iterator limit; // 마지막 요소�서 하나를 지난 곳� ��터
};
1.3. 11.3 Copy control ¶
우리가 복사, 대입, 소멸시 하는 ì�¼ì—� 대해서 명시ì �으로 ì •í•˜ì§€ 않으면 컴파ì�¼ëŸ¬ëŠ” ì •í•´ì§„ ìž�ì‹ ë§Œì�˜ 방법으로 ì�´ëŸ° ì�¼ì�„ 하는 ì •ì�˜ë¥¼ 만들어서 ìž‘ë�™í•˜ê²Œ ë�œë‹¤.
사실 ì�´ë ‡ê²Œ ìƒ�세한 ê°�ì²´ì�˜ í–‰ë�™ì–‘ì‹�ì�„ ê·œì œí• ìˆ˜ 잇는 언어로는 C++ì�´ ìœ ì�¼í•˜ë‹¤. (그만í�¼ í�´ëž˜ìŠ¤ë¥¼ ìž‘ì„±í• ë•Œ ë§Žì�€ 것들ì�´ ê³ ë ¤ë�˜ì–´ì•¼í•œë‹¤.)
사실 ì�´ë ‡ê²Œ ìƒ�세한 ê°�ì²´ì�˜ í–‰ë�™ì–‘ì‹�ì�„ ê·œì œí• ìˆ˜ 잇는 언어로는 C++ì�´ ìœ ì�¼í•˜ë‹¤. (그만í�¼ í�´ëž˜ìŠ¤ë¥¼ ìž‘ì„±í• ë•Œ ë§Žì�€ 것들ì�´ ê³ ë ¤ë�˜ì–´ì•¼í•œë‹¤.)
1.3.1. 11.3.1 Copy ìƒ�성ìž� ¶
implicit copy constructor
explicit copy constructor
복사 ìƒ�성ìž�는 ìƒ�성ìž�ì�˜ ì�´ë¦„ì�€ 기타 ìƒ�성ìž�와 같으나 ì�¸ìž�로 ìž�ì‹ ì�´ 호출ë�œ ê°�ì²´ì�˜ 타입ì�„ 갖는 ìƒ�성ìž�를 ì�˜ë¯¸í•œë‹¤.
함수ì�˜ ì�¸ìž�를 복사하는 것ì�„ í�¬í•¨í•´ì„œ, 복사본ì�„ ë§Œë“ ë‹¤ëŠ” ì�˜ë¯¸ë¥¼ ì •ì�˜í•˜ê³ 있기 때문ì—� 매개변수를 ë ˆí�¼ëŸ°ìФ 타입으로 하는 것ì�´ 좋다.
보통 ë””í�´íЏ 복사 ìƒ�성ìž�ì�˜ 경우ì—�는 í�´ëž˜ìФì�˜ 멤버 변수들ì�„ 단순히 복사만 하게 ë�©ë‹ˆë‹¤. ì�´ë•Œ 만약 ê·¸ 멤버 변수가 í�¬ì�¸í„°ì�˜ 형태ë�¼ê³ 한다면 ë¬¸ì œê°€ ë°œìƒ�하게 ë�œë‹¤.
ì�´ 경우 한쪽ì�˜ ê°�ì²´ê°€ ìˆ˜ì •ë�˜ê±°ë‚˜ 소멸ë�˜ë©´ ì�´ë¥¼ 복사한 ê°�ì²´ë�„ ê·¸ ì˜�í–¥ì�„ 받게ë�˜ê¸° 때문ì�´ë‹¤.
ë”°ë�¼ì„œ í�¬ì�¸í„°ì�˜ 경우 새로운 주소 공간ì�„ í• ë‹¹ë°›ê³ ê·¸ 대ìƒ�ì�´ ë�˜ëŠ” ê°’ì�„ 복사해야지 ì�´ëŸ° ë¬¸ì œê°€ ë°œìƒ�하지 않는다.
ì—시 다른 ìƒ�성ìž�와 마찬가지로 create() 함수를 ì�´ìš©í•´ì„œ 복사를 행하ë�„ë¡� ë§Œë“ ë‹¤.
~cpp vector<int> vi; double d; d = median (vi); // copy constructor work string line; vector<string> words = split(words); // copy constructor work
explicit copy constructor
~cpp vector<Student_info> vs; vector<Student_info> v2 = vs; // copy constructor work (from vs to v2)복사 �성�
복사 ìƒ�성ìž�는 ìƒ�성ìž�ì�˜ ì�´ë¦„ì�€ 기타 ìƒ�성ìž�와 같으나 ì�¸ìž�로 ìž�ì‹ ì�´ 호출ë�œ ê°�ì²´ì�˜ 타입ì�„ 갖는 ìƒ�성ìž�를 ì�˜ë¯¸í•œë‹¤.
함수ì�˜ ì�¸ìž�를 복사하는 것ì�„ í�¬í•¨í•´ì„œ, 복사본ì�„ ë§Œë“ ë‹¤ëŠ” ì�˜ë¯¸ë¥¼ ì •ì�˜í•˜ê³ 있기 때문ì—� 매개변수를 ë ˆí�¼ëŸ°ìФ 타입으로 하는 것ì�´ 좋다.
~cpp
template <class T> class Vec {
public:
Vec(const Vec& v); // copy constructor
// ì�´ì „ê³¼ ê°™ì�Œ
};
보통 ë””í�´íЏ 복사 ìƒ�성ìž�ì�˜ 경우ì—�는 í�´ëž˜ìФì�˜ 멤버 변수들ì�„ 단순히 복사만 하게 ë�©ë‹ˆë‹¤. ì�´ë•Œ 만약 ê·¸ 멤버 변수가 í�¬ì�¸í„°ì�˜ 형태ë�¼ê³ 한다면 ë¬¸ì œê°€ ë°œìƒ�하게 ë�œë‹¤.
ì�´ 경우 한쪽ì�˜ ê°�ì²´ê°€ ìˆ˜ì •ë�˜ê±°ë‚˜ 소멸ë�˜ë©´ ì�´ë¥¼ 복사한 ê°�ì²´ë�„ ê·¸ ì˜�í–¥ì�„ 받게ë�˜ê¸° 때문ì�´ë‹¤.
ë”°ë�¼ì„œ í�¬ì�¸í„°ì�˜ 경우 새로운 주소 공간ì�„ í• ë‹¹ë°›ê³ ê·¸ 대ìƒ�ì�´ ë�˜ëŠ” ê°’ì�„ 복사해야지 ì�´ëŸ° ë¬¸ì œê°€ ë°œìƒ�하지 않는다.
ì—시 다른 ìƒ�성ìž�와 마찬가지로 create() 함수를 ì�´ìš©í•´ì„œ 복사를 행하ë�„ë¡� ë§Œë“ ë‹¤.
~cpp
template <class T> class Vec {
public:
Vec(const Vec& v) { create(v.begin(), v.end() ); } // copy constructor
// ì�´ì „ê³¼ ê°™ì�Œ
};
1.3.2. 11.3.2 대입(Assignment) ¶
대입 연산�(assignment operator)
operator=() ì�˜ 여러가지 ì�¸ìŠ¤í„´ìŠ¤ì¤‘ì—�서 ì�¸ìž�로 ìž�ì‹ ì�˜ í�´ëž˜ìФ 형 ìž�ì²´ì—� 대한 const ë ˆí�¼ëŸ°ìŠ¤ë¥¼ 취하는 형태를 대입연산ìž�ë�¼ ì¹í•œë‹¤.
리턴값ì�„ C++ 기본 대입연산ìž�와ì�˜ ì�¼ê´€ì„±ì�˜ ìœ ì§€ë¥¼ 위해서 ì—°ì‚°ìž�ì�˜ 외쪽í•(left-hand side)ì—�대한 ë ˆí�¼ëŸ°ìŠ¤ë¥¼ 취한다.
대입 ì—°ì‚°ìž�는 복사 ì—°ì‚°ìž�와 달리 왼쪽í•ì�„ ì œê±°í•˜ê³ ìƒˆë¡œìš´ 값으로 대체시킵니다. 복사 ìƒ�성ìž�는 수행 시ì �ì—� 왼쪽í•ì�˜ ê°�ì²´ê°€ 처ì�Œ 만들어진 ìƒ�태ì�´ê¸° 때문ì—� í•´ì œí• ëŒ€ìƒ�ì�´ 없지만, 대입 ì—°ì‚°ìž�는 기존ë�°ì�´í„°ê°€ 있는 ìƒ�태ì—�서ë�„ 대입ì�„ í• ìˆ˜ 있기 때문ì—� 복사 ìƒ�성ìž�와 비슷하게 구성ë�˜ì–´ì•¼ 한다.
ë˜�한 ìž�기 대입 (self-assignment)ì�„ 위한 ê³ ë ¤ê°€ 필요하다.
operator=() ì�˜ 여러가지 ì�¸ìŠ¤í„´ìŠ¤ì¤‘ì—�서 ì�¸ìž�로 ìž�ì‹ ì�˜ í�´ëž˜ìФ 형 ìž�ì²´ì—� 대한 const ë ˆí�¼ëŸ°ìŠ¤ë¥¼ 취하는 형태를 대입연산ìž�ë�¼ ì¹í•œë‹¤.
리턴값ì�„ C++ 기본 대입연산ìž�와ì�˜ ì�¼ê´€ì„±ì�˜ ìœ ì§€ë¥¼ 위해서 ì—°ì‚°ìž�ì�˜ 외쪽í•(left-hand side)ì—�대한 ë ˆí�¼ëŸ°ìŠ¤ë¥¼ 취한다.
~cpp
template <class T> class Vec {
public:
Vec& operator=(const Vec&); // copy constructor
// ì�´ì „ê³¼ ê°™ì�Œ
};
대입 ì—°ì‚°ìž� 오버로딩시 주ì�˜í•´ì•¼í• 사í•대입 ì—°ì‚°ìž�는 복사 ì—°ì‚°ìž�와 달리 왼쪽í•ì�„ ì œê±°í•˜ê³ ìƒˆë¡œìš´ 값으로 대체시킵니다. 복사 ìƒ�성ìž�는 수행 시ì �ì—� 왼쪽í•ì�˜ ê°�ì²´ê°€ 처ì�Œ 만들어진 ìƒ�태ì�´ê¸° 때문ì—� í•´ì œí• ëŒ€ìƒ�ì�´ 없지만, 대입 ì—°ì‚°ìž�는 기존ë�°ì�´í„°ê°€ 있는 ìƒ�태ì—�서ë�„ 대입ì�„ í• ìˆ˜ 있기 때문ì—� 복사 ìƒ�성ìž�와 비슷하게 구성ë�˜ì–´ì•¼ 한다.
ë˜�한 ìž�기 대입 (self-assignment)ì�„ 위한 ê³ ë ¤ê°€ 필요하다.
~cpp
template<class T> Vec<T>& Vec<T>::operator=(const Vec& rhs) {
// self-assignment check
if (&rhs != this) {
//lhs memory deallocation
uncreate();
create(rhs.begin(), rhs.end());
{
return *this;
}
- 헤�파��서� 리턴타입� Vec& 로 사용�었�
템플릿ì�˜ 스코프 안ì—�서는 타입 매개변수를 ìƒ�략하는 ì‹�ì�˜ ë�” 쉬운 표현ì�„ ì‚¬ìš©í• ìˆ˜ 있다.
Vec::operator= ì�´ 아니ë�¼, Vec<T>::operator= ê°€ ì›�래ì�˜ ì�´ë¦„ì�´ë‹¤. 그러나 ì�¼ë‹¨ operator=ê°€ Vec<T>ì�˜ 멤버ë�¼ëŠ” 사실ì�´ ë°�혀지면 ë�”ì�´ìƒ� 템플릿 í•œì •ìž�를 ë°˜ë³µí• í•„ìš”ê°€ 없다.
- this 키워드� 사용
this는 멤버함수 안ì—�서 ìœ íš¨í•˜ë‹¤. this는 멤버함수를 호출시킨 ê°�ì²´ì�˜ í�¬ì�¸í„°ë¥¼ 리턴한다.
만약 this 키워드로 ìž�기 대입 ì²´í�¬ë¥¼ 하지 않ì�„ 경우ì—�는 오른쪽 ê°�체를 í•´ì œí•œ ë’¤ì—� 다시 왼쪽í•ì—� 대입하게 ë�˜ë¯€ë¡œ ë¬¸ì œê°€ ë°œìƒ�하게 ë�œë‹¤.
- *this � 리턴
ì§€ì— ê°�ì²´ì—� 대한 리턴ì�„ 해서는 안ë�œë‹¤. ì§€ì— ê°�체를 리턴하면 ê°�ì²´ì�˜ 존재 scope를 벗어나기 때문ì—� 올바른 ë�™ìž‘ì�„ 보장하지 못한다.
1.3.3. 11.3.3 대입ì�€ 초기화가 아니다 ¶
C++� = 가 �체� 초기화, 복사 시� ��하게 사용�기 때문� 복사 �성�와, 대입연산�� 구분� 없다.
= 가 초기화 �지 않� �체� 사용�면 복사 �성�로, 초기화� �체� 사용�면 대입 연산�로 사용�다.
�성�� 2가지 용법
리턴값�서� 복사 �성�� 사용, 대입연산�� 사용
= 가 초기화 �지 않� �체� 사용�면 복사 �성�로, 초기화� �체� 사용�면 대입 연산�로 사용�다.
| 초기화 ìƒ�황 | * 변수 ì„ ì–¸ì‹œ * 함수 진입시, 함수 매개변수 * 함수 리턴값 * ìƒ�성ìž� 초기 ì„¤ì •ìž� |
�성�� 2가지 용법
~cpp string url_ch = "~;/?:@=&$-_.+!*'(),"; string spaces(url_ch.size(), ' ');
리턴값�서� 복사 �성�� 사용, 대입연산�� 사용
~cpp vector<string> split(const string&); vector<string> v; v = split(line); // 함수� 종료시 임시 �체를 복사�성�로 �성 // �성� 임시 �체를 대입 연산�가 v � 대입
1.3.4. 11.3.4 소멸ìž�(Destructor) ¶
소멸�(destructor)
ë�™ì � í• ë‹¹ìœ¼ë¡œ ìƒ�성ë�œ ê°�체는 delete 키워드로 ì œê±°í•˜ê¸° ì „ì—�는 메모리 공간ì—�서 ì‚ì œë�˜ì§€ 않는ë�°, ê°�ì²´ 멤버변수로 ë�™ì � í• ë‹¹ì�´ ë�œ 변수가 존재한다면 ê°�ì²´ì�˜ í•´ì œì‹œì—� ì�´ 공간ì�„ 메모리ì—�서 í•´ì œí•´ì£¼ì–´ì•¼ 한다.
소멸ìž�ì�˜ 형태는 ìƒ�성ìž�와 마찬가지로 반환형ì�´ 없으며 ê°�ì²´ 형명과 ë�™ì�¼í•œ 함수명ì�„ 갖는 ëŒ€ì‹ ì—� 첫 글ìž�로 ~를 ê°€ì ¸ì•¼ 한다.
ë�™ì � í• ë‹¹ìœ¼ë¡œ ìƒ�성ë�œ ê°�체는 delete 키워드로 ì œê±°í•˜ê¸° ì „ì—�는 메모리 공간ì—�서 ì‚ì œë�˜ì§€ 않는ë�°, ê°�ì²´ 멤버변수로 ë�™ì � í• ë‹¹ì�´ ë�œ 변수가 존재한다면 ê°�ì²´ì�˜ í•´ì œì‹œì—� ì�´ 공간ì�„ 메모리ì—�서 í•´ì œí•´ì£¼ì–´ì•¼ 한다.
소멸ìž�ì�˜ 형태는 ìƒ�성ìž�와 마찬가지로 반환형ì�´ 없으며 ê°�ì²´ 형명과 ë�™ì�¼í•œ 함수명ì�„ 갖는 ëŒ€ì‹ ì—� 첫 글ìž�로 ~를 ê°€ì ¸ì•¼ 한다.
~cpp
template <class T> class Vec {
public:
~Vec() { uncreate; } // copy constructor
// ì�´ì „ê³¼ ê°™ì�Œ
};
1.3.5. 11.3.5 ë””í�´íЏ ì—°ì‚°(Default operation) ¶
ìƒ�성ìž�, 소멸ìž�, 복사 ìƒ�성ìž�, 대입연산ìž�를 ìž¬ì •ì�˜ 하지 않ì�„ 경우ì—�는 컴파ì�¼ëŸ¬ê°€ 기 ì •ì�˜ë�œ ë°©ì‹�으로 ì�´ëŸ° ìƒ�성ìž�ì�˜ 기본형ì�„ ë§Œë“ ë‹¤.
ì�´ 경우 멤버 ê°�ì²´(has-a)ì�¸ 경우ì—�는 재귀ì �으로 ì�´ëŸ° ë””í�´íЏ ì—°ì‚°ì�„ 수행하며, 기본형(primitive type)ì�˜ 경우ì—�는 ê°’ì—�ì�˜í•œ ë°©ì‹�으로 ì�´ëŸ° 연산들ì�´ 행해진다. 그러나 í�¬ì�¸í„°ì�˜ 경우 특별히 다른 ì�¼ì�´ 잃어나지 않으며, í�¬ì�¸í„°ê°€ 가리키ë�˜ 공간ì�€ í•´ì œë�˜ì§€ 않는다.
default constructor 를 사용하기 위해서는 ê°�ì²´ì�˜ 멤버들ì�´ ë””í�´íЏ ìƒ�성ìž�를 가진 경우ì—�ë§Œ ì •ìƒ�ì �ì�¸ ë�™ìž‘ì�„ ë³´ìž¥í• ìˆ˜ 있다.
ì�¼ë°˜ì �으로는 명시ì �으로 ì •ì�˜ë�œ ë””í�´íЏ ìƒ�성ìž�를 만드는 것ì�´ 좋다.
ì�´ 경우 멤버 ê°�ì²´(has-a)ì�¸ 경우ì—�는 재귀ì �으로 ì�´ëŸ° ë””í�´íЏ ì—°ì‚°ì�„ 수행하며, 기본형(primitive type)ì�˜ 경우ì—�는 ê°’ì—�ì�˜í•œ ë°©ì‹�으로 ì�´ëŸ° 연산들ì�´ 행해진다. 그러나 í�¬ì�¸í„°ì�˜ 경우 특별히 다른 ì�¼ì�´ 잃어나지 않으며, í�¬ì�¸í„°ê°€ 가리키ë�˜ 공간ì�€ í•´ì œë�˜ì§€ 않는다.
default constructor 를 사용하기 위해서는 ê°�ì²´ì�˜ 멤버들ì�´ ë””í�´íЏ ìƒ�성ìž�를 가진 경우ì—�ë§Œ ì •ìƒ�ì �ì�¸ ë�™ìž‘ì�„ ë³´ìž¥í• ìˆ˜ 있다.
ì�¼ë°˜ì �으로는 명시ì �으로 ì •ì�˜ë�œ ë””í�´íЏ ìƒ�성ìž�를 만드는 것ì�´ 좋다.
1.3.6. 11.3.6 세 법칙(rule of three) ¶
| 복사 ìƒ�성ìž� ë¯¸ì •ì�˜ | ì›�본과 복사본ì�˜ ë�°ì�´í„°ê°€ ìƒ�호 참조ì�˜ 형ì‹�으로 ë�™ìž‘한다. |
| 소멸ìž� ë¯¸ì •ì�˜ | í�¬ì�¸í„° ê°�ì²´ê°€ 존재하는 경우 memory leak ì�´ ë°œìƒ� |
| 한 ê°�ì²´ì—� 대해서 ëª¨ë“ ë³µì‚¬ë³¸ì�´ ì œëŒ€ë¡œ ë�™ìž‘하면 ìž¬ì •ì�˜ í•´ì•¼í• ê²ƒë“¤ |
| T::T() T::~T() T::T(const T&) T::operator=(const T&) |
Rule of three : 복사 ìƒ�성ìž�, 소멸ìž�, 대입 ì—°ì‚°ìž�ê°€ ë°€ì ‘í•œ 관계를 갖는 것ì�„ ì�¼ì»¬ì–´ 표현하는 ë§�ì�´ë‹¤.
1.4. 11.4 Dynamic Vecs ¶
push_back� 구현방�
push_backì�„ 구현함ì—� 있어서 요소 ë°°ì—´ì�˜ í�¬ê¸°ë¥¼ 키워야하는ë�° ì�´ 것ì�„ 위해서 여기서는 필요양 보다 ë§Žì�€ 수ì�˜ ë°°ì—´ì�„ í• ë‹¹í•˜ê³ í•˜ë‚˜ì”© 변수를 하나ë�” 만들어서 현재 사용가능한 ë��ì�„ 가리키는ë�° ì�´ìš©í•˜ê¸°ë¡œ 한다.
push_backì�„ 구현함ì—� 있어서 요소 ë°°ì—´ì�˜ í�¬ê¸°ë¥¼ 키워야하는ë�° ì�´ 것ì�„ 위해서 여기서는 필요양 보다 ë§Žì�€ 수ì�˜ ë°°ì—´ì�„ í• ë‹¹í•˜ê³ í•˜ë‚˜ì”© 변수를 하나ë�” 만들어서 현재 사용가능한 ë��ì�„ 가리키는ë�° ì�´ìš©í•˜ê¸°ë¡œ 한다.
~cpp
template<class T> class Vec {
public:
size_type size() const { return avail - data; }
iterator end() { return avail; }
const_iterator end() const { return avail; }
void push_back(const T& val) {
if (avail == limit)
grwo(0;
unchecked_append(val);
}
private:
iterator data;
iterator avail;
iterator limit;
}
1.5. 11.5 Flexible memory management ¶
Vecì—�서 new, delete 키워드를 사용하지 않는다. ë°°ì—´ì�„ new로 í• ë‹¹í•˜ê²Œ ë�˜ë©´ 기본ì �으로 ìƒ�성ë�˜ëŠ” ê°�ì²´ì�˜ ë””í�´íЏ ìƒ�성ìž�로 초기화 ë�˜ê¸° 때문ì�´ë‹¤. ì�´ëŠ” ì›�래 우리가 ì�˜ë�„한 것과는 다른 ë°©ì‹�으로 ë�™ìž‘한다.
ë”°ë�¼ì„œ 우리는 좀ë�” ì�¼ë°˜ì �ì�¸ 형태ì�˜ 표준 ì œê³µ ë�¼ì�´ë¸ŒëŸ¬ë¦¬ì—� 존재하는 관리ìž�를 사용한다.
ë”°ë�¼ì„œ 우리는 좀ë�” ì�¼ë°˜ì �ì�¸ 형태ì�˜ 표준 ì œê³µ ë�¼ì�´ë¸ŒëŸ¬ë¦¬ì—� 존재하는 관리ìž�를 사용한다.
<memory> ì�˜ allocate<> STLì—�서 ì•Œì•„ì•¼í• ì¤‘ìš” 메소드
allocate �래스� 비멤버 함수 �부
~cpp
template<class T> class allocator {
pubilc:
T* allocate(size_t);
void deallocate(T*, size_t);
void construct(T*, T);
void destroy(T*);
};
| allocate, deallocate | ì‹¤ì œ 메모리ì�˜ 공간ì�„ í• ë‹¹, í•´ì œí•œë‹¤. 그러나 í• ë‹¹ì‹œ 초기화는 하지 않는다. |
| construct, destroy | í• ë‹¹ë�œ 메모리 공간ì—� ì �당한 ë�°ì�´í„° 타입ì�„ 초기화 시킨다. (공간ì�€ allocate를 통해서 ì�´ë¯¸ í• ë‹¹ë�œ 공간ì�´ì–´ì•¼ 한다.) |
allocate �래스� 비멤버 함수 �부
~cpp template<class In, class For> For uninitialized_copy(In, In, For); tempalte<class for, class T> void uninitialized_fill(For, For, const T&);
| uninitialized_copy | 첫번째와 �번째��� 스퀀스만�� 3번째 ��� 공간� 복사한다. |
| uninitialized_fill | 첫번째와 �번째 ��� 공간� T로 채운다. |
1.5.1. 11.5.1 최종 Vec í�´ëž˜ìФ ¶
~cpp
//vec.h
#ifndef VEC_H
#define VEC_H
#include <algorithm>
#include <cstddef>
#include <memory>
using std::max;
template <class T> class Vec {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
Vec() { create(); }
explicit Vec(size_type n, const T& t = T()) { create(n, t); }
Vec(const Vec& v) { create(v.begin(), v.end()); }
Vec& operator=(const Vec&); // as defined in 11.3.2/196
~Vec() { uncreate(); }
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }
void push_back(const T& t) {
if (avail == limit)
grow();
unchecked_append(t);
}
size_type size() const { return avail - data; } // changed
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return avail; } // changed
const_iterator end() const { return avail; } // changed
void clear() { uncreate(); }
bool empty() const { return data == avail; }
private:
iterator data; // first element in the `Vec'
iterator avail; // (one past) the last element in the `Vec'
iterator limit; // (one past) the allocated memory
// facilities for memory allocation
std::allocator<T> alloc; // object to handle memory allocation
// allocate and initialize the underlying array
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
// destroy the elements in the array and free the memory
void uncreate();
// support functions for `push_back'
void grow();
void unchecked_append(const T&);
};
template <class T> void Vec<T>::create()
{
data = avail = limit = 0;
}
template <class T> void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
std::uninitialized_fill(data, limit, val);
}
template <class T>
void Vec<T>::create(const_iterator i, const_iterator j)
{
data = alloc.allocate(j - i);
limit = avail = std::uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate()
{
if (data) {
// destroy (in reverse order) the elements that were constructed
iterator it = avail;
while (it != data)
alloc.destroy(--it);
// return all the space that was allocated
alloc.deallocate(data, limit - data);
}
// reset pointers to indicate that the `Vec' is empty again
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow()
{
// when growing, allocate twice as much space as currently in use
size_type new_size = max(2 * (limit - data), ptrdiff_t(1));
// allocate new space and copy existing elements to the new space
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
// return the old space
uncreate();
// reset pointers to point to the newly allocated space
data = new_data;
avail = new_avail;
limit = data + new_size;
}
// assumes `avail' points at allocated, but uninitialized space
template <class T> void Vec<T>::unchecked_append(const T& val)
{
alloc.construct(avail++, val);
}
template <class T>
Vec<T>& Vec<T>::operator=(const Vec& rhs)
{
// check for self-assignment
if (&rhs != this) {
// free the array in the left-hand side
uncreate();
// copy elements from the right-hand to the left-hand side
create(rhs.begin(), rhs.end());
}
return *this;
}
#endif
�래스 불변�(class invariant)- data는 첫번째 요소, 없다면 0� 가진다.
- data<=avail<=limit
- �성� 요소는 [data, avail)� 범위� 존재
- �성�지 않� 요소는 [avail, limit)� 범위� 존재
~cpp template <class T> void Vec<T>::create() { data = avail = limit = 0; // ë””í�´íЏ ìƒ�성ìž� ì�´ê¸° 때문ì—� í�¬ì�¸í„°ì—� ë„�í�¬ì�¸í„°ë¥¼ 넣어서 빈 ê°�ì²´ìž„ì�„ 나타낸다. } template <class T> void Vec<T>::create(size_type n, const T& val) { data = alloc.allocate(n); // ì�¸ìž�로 ë°›ì�€ ë§Œí�¼ì�˜ 공간ì�„ í• ë‹¹í•œë‹¤. limit = avail = data + n; // ë��ì�„ 가리킨다. 현재로서는 초기화시 nê°œì�˜ 공간ì�„ í• ë‹¹í–ˆê¸° 때문ì—� avail = limit ì�´ë‹¤. std::uninitialized_fill(data, limit, val); // ì•„ì§� 초기화 ë�˜ì§€ 않ì�€ 공간ì—� 여러개ì�˜ ê°�체를 삽입해야하기 때문ì—� 비멤버함수를 ì�´ìš©í•´ì„œ 초기화 시킨다. } template <class T> void Vec<T>::create(const_iterator i, const_iterator j) { data = alloc.allocate(j - i); limit = avail = std::uninitialized_copy(i, j, data); }주ì„�ì�„ ì°¸ê³ í•˜ê³ ì�´í•´í•˜ê¸° 바람.
~cpp template <class T> void Vec<T>::uncreate() { if (data) { // destroy (in reverse order) the elements that were constructed iterator it = avail; while (it != data) alloc.destroy(--it); // return all the space that was allocated alloc.deallocate(data, limit - data); } // reset pointers to indicate that the `Vec' is empty again data = limit = avail = 0; }초기화 �어있는 내부� 요소들� 하나씩 destroy함수를 �용해서 소멸시킨다.
그후 í• ë‹¹ë�œ 공간ì�„ deallocate 함수를 ì�´ìš©í•´ì„œ 메모리 공간ìƒ�ì—�서 í•´ì œ 시킨다.
í•´ì œê°€ ë��난 ì�´í›„ì—�는 다시 빈 백터로 나태내기위해서 data, limit, avail ì�„ 초기화 한다.
~cpp template <class T> void Vec<T>::grow() { // when growing, allocate twice as much space as currently in use size_type new_size = max(2 * (limit - data), ptrdiff_t(1)); // 비어있는 벡터ì�¸ 경우ì—�는 1개만 í• ë‹¹í•œë‹¤. // allocate new space and copy existing elements to the new space iterator new_data = alloc.allocate(new_size); iterator new_avail = std::uninitialized_copy(data, avail, new_data); // return the old space uncreate(); // reset pointers to point to the newly allocated space data = new_data; avail = new_avail; limit = data + new_size; } // assumes `avail' points at allocated, but uninitialized space template <class T> void Vec<T>::unchecked_append(const T& val) { alloc.construct(avail++, val); }grow 함수는 한번 í˜¸ì¶œí• ë•Œ ì�´ë¯¸ 워래 공간ì�˜ 2배를 í• ë‹¹ì‹œí‚¤ê¸° 때문ì—� 계ì†�ë�œ push_back로 ì�¸í•œ 메모리 í• ë‹¹ 오버헤드를 줄ì�¼ 수 있다.
unchecked_append()함수는 grow() 함수ì�˜ 바로 ë’¤ì—�서만 ë�™ìž‘하ë�„ë¡� 구성했기 때문ì—� ë�™ìž‘ì�˜ ì•ˆì •ì„±ì�„ ë³´ìž¥í• ìˆ˜ 있다. (ë¬¼ë¡ ë¬¸ì„œë¡œ 남겨서 ì�´í›„ í�´ëž˜ìФ 확장시 다른 프로그래머가 알 수 있ë�„ë¡� í•´ì•¼í• ë“¯)










