- (C++11~) get_new_handler()가 추가되었습니다.
- (C++11~) pointer_traits가 추가되어 포인터와 유사한 타입들을 다루는 표준화된 방법을 제공합니다.
- (C++11~) aligned_alloc()이 추가되어 정렬된 메모리를 할당할 수 있습니다.
- (C++11~) addressof()가 추가되어
operator &()가 연산자 오버로딩 되었어도 실제 주소를 구할 수 있습니다.
- (C++11~) align()이 추가되어 메모리 정렬했을 때의 포인터를 구할 수 있습니다.
- (C++11~) allocator_traits, allocator_arg, uses_allocator, scoped_allocator_adaptor가 추가되었습니다.
- (C++11~) uninitialized_copy_n이 추가되었습니다.
- (C++11~) 가비지 컬렉터 지원이 추가되었습니다.
- (C++17~) integer_sequence가 추가되어 컴파일 타임에 정수 타입의 시퀀스를 만들 수 있습니다.
- (C++17~) align_val_t가 추가되었습니다.
- (C++17~) launder()가 추가되어 위치 지정 생성으로 생성된 개체의 합법적인 포인터를 얻을 수 있습니다.
- (C++17~) polymorphic_allocator가 추가되어 할당시 런타임 다형성을 지원합니다. 메모리 리소스를 사용하여 메모리 풀을 손쉽게 만들 수 있습니다.
- (C++17~) uninitialized_move(), uninitialized_default_construct(), uninitialized_value_construct(), destroy(), destroy_at()가 추가되어 위치 지정 생성자 호출과 소멸자 호출의 새로운 방법을 제공합니다.
- (C++20~) to_address(), assume_aligned()가 추가되었습니다.
- (C++20~) destroying_delete가 추가되었습니다.
- (C++20~) uses_allocator_construction_args, make_obj_using_allocator, uninitialized_construct_using_allocator가 추가되었습니다.
- (C++20~) construct_at()이 추가되었습니다.
C스타일 메모리 관리
| 항목 |
내용 |
| malloc() |
메모리를 할당합니다. |
calloc() |
(작성중) |
realloc() |
(작성중) |
| free() |
메모리를 해제합니다. |
aligned_alloc() (C++17~) |
정렬된 메모리를 할당합니다. |
저수준 메모리 관리
launder()는 위치 지정 생성으로 생성한 개체의 포인터를 다룰때 합법적인 방법을 제공합니다.
다음예에서 ptr과 newPtr은 위치 지정 생성을 이용하여 동일한 메모리 공간에 만들어 집니다. 그래서, ptr로 접근하던, newPtr로 접근하던 동일한 데이터여야 합니다. 하지만 이전 이름인 ptr로 다루는 것이 찜찜하기도 한데요,
그냥 찜찜할 뿐만 아니라 const인 경우는 컴파일러 최적화에 의해 이전 값인 1이 될 수도 있습니다. 그래서 보통 사용하지 않는데요,
launder()를 사용하면, newPtr로 위치 지정 생성을 했더라도 이전 이름인 ptr로 접근할 수 있습니다. 따라서 std::launder(ptr)->m_X == 3와 같이 이전 개체 이름인 ptr로 newPtr이 생성한 값에 합법적으로 접근할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| class A {
public:
const int m_X; // const 멤버 입니다.
int m_Y;
A(int x, int y) : m_X{x}, m_Y{y} {}
};
A* ptr = new A{1, 2};
EXPECT_TRUE(ptr->m_X == 1 && ptr->m_Y == 2);
// 위치 지정 생성으로 ptr 위치에 다시 생성합니다.
A* newPtr = new(ptr) A{3, 4};
EXPECT_TRUE(newPtr->m_X == 3 && newPtr->m_Y == 4);
// 이전 포인터인 ptr로 다루려면, launder를 사용합니다.
EXPECT_TRUE((ptr->m_X == 1 || ptr->m_X == 3)); // (△) 비권장. 컴파일러 최적화에 의해 const 변수는 이전값 1을 가질 수도 있습니다.
EXPECT_TRUE(ptr->m_Y == 4); // (△) 비권장. 불법입니다만, 잘 동작합니다.
EXPECT_TRUE(std::launder(ptr)->m_X == 3); // launder를 이용하여 합법적으로 사용할 수 있습니다.
delete std::launder(ptr);
|
(C++11~) pointer_traits
포인터와 유사한 타입들을 다루는 표준화된 방법을 제공합니다.
| 항목 |
내용 |
pointer (C++11~) |
포인터 타입입니다. |
element_type (C++11~) |
포인터가 가리키는 값의 타입입니다. |
difference_type (C++11~) |
포인터끼리 뺀 값의 타입입니다. |
template<typename U> using rebind (C++11~) |
U에 바인딩되는 타입입니다. |
pointer pointer_to(element_type& r); (C++11~) |
element_type을 pointer 타입으로 변환합니다. |
(C++11~) addressof()
operator &()가 연산자 오버로딩 되었어도 실제 주소를 리턴합니다.
1
2
| template<typename T>
T* addressof(T& arg) noexcept;
|
(C++11~) align()
space 크기의 버퍼 공간에 size크기의 개체를 alignment 로 메모리 정렬했을때의 연속된 메모리 포인터인 ptr을 구합니다.
1
2
3
4
5
6
| void* align(
std::size_t alignment, // 메모리 정렬, alignof() 값
std::size_t size, // 졍렬할 요소의 크기
void*& ptr, // 최소 space 바이트의 연속된 메모리 포인터
std::size_t& space // 작업할 버퍼의 최대 크기
);
|
할당자
기존에는 allocator만 있었으나(할당자(Allocator) 참고), 좀더 많은 내용이 보강되었습니다.
| 항목 |
내용 |
| allocator |
컨테이너 등 STL에서 개체 할당에 사용합니다. |
allocator_traits (C++11~) |
(작성중) |
allocator_arg (C++11~) |
(작성중) |
uses_allocator (C++11~) |
(작성중) |
scoped_allocator_adaptor (C++11~) |
(작성중) |
| polymorphic_allocator (C++17~) |
런타임 다형성을 지원하는 할당자입니다. 메모리 리소스를 사용하여 메모리 풀을 손쉽게 만들 수 있습니다. |
uses_allocator_construction_args (C++20~) |
(작성중) |
make_obj_using_allocator (C++20~) |
(작성중) |
uninitialized_construct_using_allocator (C++20~) |
(작성중) |
allocator_result (C++23~) |
(작성중) |
메모리 유틸리티 작업
C++17 부터는 uninitialized_move(), uninitialized_default_construct(), uninitialized_value_construct(), destroy(), destroy_at()가 추가되어 위치 지정 생성자 호출과 소멸자 호출의 새로운 방법을 제공합니다.
| 항목 |
내용 |
uninitialized_copy()
uninitialized_copy_n() (C++11~) |
메모리를 복사합니다. |
uninitialized_fill()
uninitialized_fill_n() |
메모리를 특정 값으로 채웁니다. |
uninitialized_move() (C++17~)
uninitialized_move_n() (C++17~) |
메모리의 값을 이동시킵니다. |
uninitialized_default_construct() (C++17~)
uninitialized_default_construct_n() (C++17~) |
주어진 메모리 영역 개체들을 기본 생성자(new T)로 초기화 합니다. 자동 제로 초기화를 하지 않습니다. |
uninitialized_value_construct() (C++17~)
uninitialized_value_construct_n() (C++17~) |
주어진 메모리 영역 개체들을 값 생성자(new T())로 초기화 합니다. 자동 제로 초기화를 합니다. |
destroy() (C++17~)
destroy_n() (C++17~) |
주어진 메모리 영역 개체들의 소멸자를 호출합니다. |
destroy_at() (C++17~) |
주어진 메모리 영역 개체의 소멸자를 호출합니다. |
construct_at() (C++20~) |
주어진 메모리 영역 개체의 생성자를 호출합니다. |
다음과 같은 T개체가 있다고 할때,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| class T {
private:
int m_X;
int m_Y;
public:
T() :
m_X{10},
m_Y{20} {
std::cout << "T : Constructor" << std::endl;
}
~T() {
std::cout << "T : Destructor" << std::endl;
}
int GetX() const {return m_X;}
int GetY() const {return m_Y;}
};
|
다음은 스택에 버퍼를 생성하고, 3개의 T 개체를 생성하고 소멸시키는 예입니다.
위치 지정 생성 방식을 사용하면 다음과 같고,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| unsigned char buffer[sizeof(T) * 3]; // T 개체 3개를 저장할 수 있는 스택 영역을 확보합니다.
// 3개의 생성자 호출
T* t1 = new(static_cast<void*>(buffer + sizeof(T) * 0)) T;
T* t2 = new(static_cast<void*>(buffer + sizeof(T) * 1)) T;
T* t3 = new(static_cast<void*>(buffer + sizeof(T) * 2)) T;
EXPECT_TRUE(t1->GetX() == 10 && t1->GetY() == 20);
EXPECT_TRUE(t2->GetX() == 10 && t2->GetY() == 20);
EXPECT_TRUE(t3->GetX() == 10 && t3->GetY() == 20);
// 3개의 소멸자 호출
t1->~T();
t2->~T();
t3->~T();
|
uninitialized_default_construct() 와 destroy()를 사용한 방식은 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| unsigned char data[sizeof(T) * 3]; // T 개체 3개를 저장할 수 있는 스택 영역을 확보합니다.
auto begin{reinterpret_cast<T*>(data)};
auto end{begin + 3};
// 요소 3개의 생성자 호출
std::uninitialized_default_construct(begin, end);
EXPECT_TRUE((begin + 0)->GetX() == 10 && (begin + 0)->GetY() == 20);
EXPECT_TRUE((begin + 1)->GetX() == 10 && (begin + 1)->GetY() == 20);
EXPECT_TRUE((begin + 2)->GetX() == 10 && (begin + 2)->GetY() == 20);
// 요소 3개의 소멸자 호출
std::destroy(begin, end);
|
실행 결과를 보면 두 경우 모두 생성자 3회, 소멸자 3회 호출된 것을 알 수 있습니다.
1
2
3
4
5
6
| T : Constructor
T : Constructor
T : Constructor
T : Destructor
T : Destructor
T : Destructor
|
(C++11~)가비지 컬렉터 지원
| 항목 |
내용 |
declare_reachable() (C++11~C++23) |
(작성중) |
undeclare_reachable() (C++11~C++23) |
(작성중) |
declare_no_pointers() (C++11~C++23) |
(작성중) |
undeclare_no_pointers() (C++11~C++23) |
(작성중) |
pointer_safety (C++11~C++23) |
(작성중) |
get_pointer_safety() (C++11~C++23) |
(작성중) |
초기화 되지 않은 스토리지
| 항목 |
내용 |
raw_storage_iterator (~C++17) |
(작성중) |
get_temporary_buffer (~C++17) |
(작성중) |
return_temporary_buffer (~C++17) |
(작성중) |
(C++20~) 메모리 관리
| 항목 |
내용 |
| to_address() (C++20~) |
일반 포인터나 스마트 포인터 구분없이 실제 관리되는 메모리 주소를 접근하는 일관된 방법을 제공합니다. |
assume_aligned(ptr) (C++20~) |
ptr을 메모리 정렬된 포인터로 변환합니다. |
to_address()는 실제 메모리에서 관리되는 포인터를 구하는 일관된 방법을 제공합니다.
1
2
3
4
5
6
| int* rawPtr{new int{0}};
std::shared_ptr<int> sp{rawPtr};
EXPECT_TRUE(rawPtr == std::to_address(rawPtr)); // 일반 포인터나 스마트 포인터 구분없이 실제 관리되는 메모리 주소를 접근하는 일관된 방법을 제공합니다.
EXPECT_TRUE(rawPtr == std::to_address(sp));
EXPECT_TRUE(rawPtr == sp.get());
|
(C++23~) 스마트 포인트 어뎁터
| 항목 |
내용 |
out_ptr_t(C++23~)
out_ptr() (C++23~) |
(작성중) |
inout_ptr_t(C++23~)
inout_ptr() (C++23~) |
(작성중) |
(C++23~) 명시적 수명 관리
| 항목 |
내용 |
start_lifetime_as (C++23~)
start_lifetime_as_array (C++23~) |
(작성중) |
댓글남기기