On this page
article
C++ Make Vector
Generic Container Idioms
개념
일반화 컨테이너를 설계할 때 저장되는 타입이 가져야 하는 요구조건을 최소화하는 기술.
실현
Vector<Point> vec(10);
대신 Vector<Point> vec(vec, 10);
방식으로 사용하게 설계한다.
이렇게 하면 복사 생성자를 이용해 원소를 만들게 하므로 T 타입에 기본 생성자가 없어도 된다.
이를 위해 메모리 할당과 생성자 호출을 분리하여 구현한다.
예시
#include <iostream>
#include "Point.h"
template<typename T>
class Vector {
T* buff;
std::size_t size;
std::size_t capacity;
public:
Vector(std::size_t sz, const T& value = T() ) : size(sz), capacity(sz) {
buff = static_cast<Point*>(operator new(sizeof(Point) * sz));
int i = 0;
try {
for (i = 0; i < sz; i++) {
new(&buff[i]) T(value);
}
} catch(...) {
for (int j = i - 1; j >= 0; --j)
buff[j].~T();
operator delete(buff);
size = 0;
capacity = 0;
throw;
}
}
~Vector() {
for (int j = size - 1; j >= 0; --j)
buff[j].~T();
operator delete(buff);
}
};
Allocator
상황
메모리 할당 해지 방법을 바꾸고 싶다.
std::allocator
메모리 할당 관련 함수를 추상화한 도구.
allocate
construct
destroy
deallocate
예시
std::allocator<Point> ax;
Point* p2 = ax.allocate(1);
ax.construct(p2, 0, 0);
ax.destroy(p2);
allocator_traits
construct
, destroy
의 기본 구현 제공.
사용자 함수가 있으면 그걸 사용한다.
예시
std::allocator_traits<MyAlloc<Point>>::construct(ax, p1, 0, 0);
Policy Based Design
개념
클래스가 사용하는 정책을 템플릿 인자로 전달받아 교체 가능하게 만드는 디자인.
예시
template<typename T, typename Alloc = std::allocator<T>>
class Vector {
T* buff;
std::size_t size;
std::size_t capacity;
Alloc ax;
public:
Vector(std::size_t sz, const T& value = T() ) : size(sz), capacity(sz) {
buff = std::allocator_traits<Alloc>::allocate(ax, sizeof(T) * sz);
int i = 0;
try {
for (i = 0; i < sz; i++)
std::allocator_traits<Alloc>::construct(ax, &buff[i], value);
} catch(...) {
for (int j = i - 1; j >= 0; --j)
std::allocator_traits<Alloc>::destroy(ax, &buff[j]);
std::allocator_traits<Alloc>::deallocate(ax, buff, capacity);
size = 0;
capacity = 0;
throw;
}
}
~Vector() {
for (int j = size - 1; j >= 0; --j)
std::allocator_traits<Alloc>::destroy(ax, &buff[j]);
std::allocator_traits<Alloc>::deallocate(ax, buff, capacity);
}
};
Rebind
상황
현재 allocator는 bool
단위로 할당하는데 int
단위로 할당하고 싶다.
기존에는 allocator 내부에 rebind
구조체를 정의했음.
rebind
는 template type을 바꾼 allocator type을 꺼내주는 역할.
C++11 이후 allocator_traits
로 대체됨.
C++20부터는 allocator 안에서 rebind
자체가 삭제됨.
구현
#include <iostream>
template<typename T>
class MyAlloc {
public:
using value_type = T; // allocator_traits
};
template<typename T>
void foo(T ax) {
typename T::template rebind<int>::other ax1; // C++98
std::cout << typeid(ax1).name() << std::endl;
typename std::allocator_traits<T>::template rebind_alloc<int> ax2; // C++11~
std::cout << typeid(ax2).name() << std::endl;
}
int main() {
std::allocator<bool> ax;
foo(ax);
}
Temporary Proxy
개념
임시 객체를 사용해서 **대행자(proxy)**를 만드는 기술.
예시
template<typename Alloc>
class Vector<bool, Alloc> {
struct BitProxy {
int* buff;
int idx;
BitProxy(int* buff, int idx) : buff(buff), idx(idx) {}
BitProxy& operator=(bool value) {
// 비트 단위 할당 로직
return *this;
}
operator bool() {
// 비트값 반환 로직
return true;
}
};
BitProxy operator[](int idx) { return BitProxy(buff, idx); }
};
int main() {
Vector<bool> v2(100, false);
v2[0] = true; // v2.buff 0번째 비트 설정
bool b = v2[0]; // BitProxy.operator bool()
}
vector<bool>
의 경우 참조를 반환하지 못하므로 임시 객체로 proxy를 사용한다.
따라서 vector<bool>
은 int& ref = v[0];
같은 코드는 사용할 수 없다.