U E D R , A S I H C RSS

AcceleratedC++/Chapter11

No older revisions available

No older revisions available



1. Chapter 11 Defining abstract data types

3장에서 작성한 Student_info 타입은 복사, 대입, 소멸시에 어떤 일이 수행되는지 명세되어있지 않음.
실제로 클래스를 제작할 경우에는 클래스의 생성, 복사, 대입, 소멸의 고려하여 설계되어야한다.
이장에서는 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. 11.2 Implementing the Vec class

1.2.1. 11.2.1 메모리 할당

연산을 결정한후에는 어떤 방식으로 Vec를 구현할지를 결정하는 것이다.
여기서는 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개는 최소한 구현해야한다.
~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로서 정의하는 것이 합당하다.

~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++이 유일하다. (그만큼 클래스를 작성할때 많은 것들이 고려되어야한다.)

1.3.1. 11.3.1 Copy 생성자

implicit copy constructor
~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)에대한 레퍼런스를 취한다.
~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가지 용법
~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 키워드로 제거하기 전에는 메모리 공간에서 삭제되지 않는데, 객체 멤버변수로 동적 할당이 된 변수가 존재한다면 객체의 해제시에 이 공간을 메모리에서 해제해주어야 한다.
소멸자의 형태는 생성자와 마찬가지로 반환형이 없으며 객체 형명과 동일한 함수명을 갖는 대신에 첫 글자로 ~를 가져야 한다.
~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 를 사용하기 위해서는 객체의 멤버들이 디폴트 생성자를 가진 경우에만 정상적인 동작을 보장할 수 있다.
일반적으로는 명시적으로 정의된 디폴트 생성자를 만드는 것이 좋다.

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을 구현함에 있어서 요소 배열의 크기를 키워야하는데 이 것을 위해서 여기서는 필요양 보다 많은 수의 배열을 할당하고 하나씩 변수를 하나더 만들어서 현재 사용가능한 끝을 가리키는데 이용하기로 한다.
~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에서 알아야할 중요 메소드
~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() 함수의 바로 뒤에서만 동작하도록 구성했기 때문에 동작의 안정성을 보장할 수 있다. (물론 문서로 남겨서 이후 클래스 확장시 다른 프로그래머가 알 수 있도록 해야할 듯)



Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:22:25
Processing time 0.0426 sec