9`std::vector`는 용량(capacity), 확장(growth), 재할당(reallocation) 및 반복자 유효성(iterator stability)을 어떻게 관리하나요?
`std::vector`는 요소를 연속된 메모리 공간에 저장하며, 크기(size)와 용량(capacity)을 모두 추적합니다. `size`는 생성된 요소의 개수이며, `capacity`는 추가 할당 없이 사용할 수 있도록 이미 확보된 저장 공간의 크기입니다. 요소를 추가할 때 용량을 초과하면, `vector`는 일반적으로 구현체에 정의된 기하급수적 확장 전략에 따라 더 큰 메모리 블록을 할당하고, 기존 요소를 새 위치로 이동하거나 복사한 뒤 이전 요소를 소멸시키고 기존 메모리를 해제합니다. `reserve(n)`은 크기 변경 없이 용량만 늘리며, `resize(n)`은 요소를 생성하거나 소멸시켜 크기를 변경합니다. 재할당이 발생하면 기존 요소에 대한 모든 반복자, 참조, 포인터가 무효화됩니다. 재할당이 발생하지 않더라도 `insert`나 `erase` 같은 연산은 변경 지점 및 그 이후의 위치를 가리키는 반복자 등을 무효화할 수 있습니다.
std::vector<int> v;
v.reserve(100); // capacity >= 100, size == 0
v.push_back(1); // size == 1
v.resize(10); // size == 10, adds nine zero-initialized ints
std::cout << v.size() << " " << v.capacity() << "\n";
AI 코치와 함께 이 질문에 답해 보세요
10연속 메모리 기반 표준 컨테이너와 노드 기반 표준 컨테이너에 대해 숙지해야 할 무효화 규칙은 무엇인가요?
무효화 규칙은 컨테이너와 수행하는 연산에 따라 달라집니다. vector 및 string과 같은 연속(contiguous) 컨테이너는 반복자/참조 안정성이 취약합니다. 요소가 추가되어 크기가 늘어날 때 재할당이 발생하면 모든 반복자, 참조, 포인터가 무효화됩니다. 또한 재할당이 일어나지 않더라도 insert나 erase 연산은 요소를 이동시키므로 변경이 발생한 위치 및 그 이후 위치의 요소들이 무효화됩니다. list, map, set 및 이들의 multi 변형과 같은 노드 기반 정렬 컨테이너는 insert 연산 시 삭제되지 않은 기존 요소에 대한 반복자와 참조가 일반적으로 유지됩니다. 요소를 삭제(erase)하면 삭제된 해당 요소에 대한 반복자/참조만 무효화됩니다. 순서가 없는(unordered) 컨테이너 역시 요소를 노드에 저장하므로 재해시(rehash)가 발생해도 요소에 대한 참조와 포인터는 대체로 유지되지만, 재해시는 반복자를 무효화합니다. deque는 분할 메모리 구조(segmented-storage)에 따른 독자적인 규칙을 따릅니다. 실무에서는 컨테이너 수정 작업을 거치면서 반복자나 참조를 유지해야 할 때 해당 컨테이너와 연산별 세부 규칙을 반드시 확인해야 합니다.
std::map<int, std::string> m = {{1,"a"}, {2,"b"}, {3,"c"}};
for (auto it = m.begin(); it != m.end(); ) {
if (it->first % 2 == 1)
it = m.erase(it); // returns next iterator
else
++it;
}
AI 코치와 함께 이 질문에 답해 보세요
11백엔드 룩업 테이블 관점에서 std::map, std::unordered_map, 그리고 플랫 맵(flat-map) 스타일 컨테이너를 비교해 주세요.
std::map은 보통 트리 기반으로 구현된 정렬 연관 컨테이너로, 탐색·삽입·삭제에 로그 시간이 소요됩니다. 정렬된 순회, 범위 쿼리, 또는 순서 보장이 중요할 때 유용합니다. std::unordered_map은 해시 테이블 기반으로 키 순서를 보장하지 않지만 평균적으로 상수 시간의 정확한 키 연산을 제공하며, 해싱 품질이 좋을 때 대규모 가변 룩업 테이블의 좋은 기본 선택지가 됩니다. 플랫 맵 스타일 컨테이너는 정렬된 키/값 쌍을 연속된 메모리에 저장하여 캐시 지역성이 우수하고 순회 및 이진 탐색이 빠르지만, 중간 삽입과 삭제에는 선형 시간이 소요됩니다. 백엔드 룩업 테이블용 컨테이너를 고를 때는 순서/범위 지원 필요 여부, 단순 조회 중심인지, 잦은 변경이 발생하는지, 지연 시간의 예측 가능성, 메모리 오버헤드, 캐시 동작 특성 등을 고려해야 합니다.
// Exact lookup, frequently updated:
std::unordered_map<std::string, User> users_by_id;
// Need sorted iteration or lower_bound/range queries:
std::map<std::string, User> users_by_id_ordered;
// Build once, query many times: vector sorted by key is a common flat-map style.
std::vector<std::pair<std::string, User>> users;
std::sort(users.begin(), users.end(), [](auto const& a, auto const& b) {
return a.first < b.first;
});
auto it = std::lower_bound(users.begin(), users.end(), std::string_view{"u123"},
[](auto const& p, std::string_view key) { return p.first < key; });
AI 코치와 함께 이 질문에 답해 보세요
12std::optional과 값이 없는 상태를 표현하기 위해 백엔드에서 일반적으로 활용되는 사용 사례를 설명해 주세요.
`std::optional<T>`는 포함된 T 값 또는 값이 없는 상태 중 하나를 나타냅니다. 비어 있는 상태는 `std::nullopt`로 표현되며, 코드에서는 `has_value()`를 확인하거나 불리언(boolean) 문맥에서 optional을 평가할 수 있고, `*`나 `value()`로 값에 접근하며, `value_or()`를 통해 기본값을 제공할 수 있습니다. 백엔드 코드에서는 null이 허용되는 데이터베이스 필드, 선택적 요청/설정 필드, 부재가 예상되는 캐시나 리포지토리 미스, 그리고 -1이나 빈 문자열 같은 센티널 값이 모호할 수 있는 도메인 상태를 다룰 때 유용합니다. 이는 다형성이나 상세한 오류 정보를 모델링하기 위한 것이 아니라, 오직 값의 부재만을 모델링합니다.
struct UserProfile {
std::string id;
std::optional<std::string> display_name; // absent if user has not set it
};
std::string label(UserProfile const& u) {
return u.display_name.value_or("anonymous");
}
AI 코치와 함께 이 질문에 답해 보세요
13std::string_view와 std::span은 무엇이며, 비소유 뷰(non-owning view)는 어떤 수명 위험을 유발하나요?
std::string_view는 연속된 문자 시퀀스를 가리키는 비소유 뷰이며, std::span<T>는 T 타입의 연속된 시퀀스를 가리키는 비소유 뷰입니다. 두 타입 모두 메모리를 할당하거나 소유하지 않고 포인터와 길이 정보만 전달하므로, 제로 카피 매개변수나 버퍼 API에 매우 유용합니다. 주요 위험 요소는 객체 수명입니다. 뷰가 참조하는 원본 스토리지는 뷰 자체보다 수명이 길어야 하며, 뷰를 사용하는 동안 무효화(invalidation)되지 않아야 합니다. 임시 객체, 지역 객체, 이미 소멸된 객체, 또는 재할당된 컨테이너를 가리키는 뷰를 반환하거나 저장하면 댕글링 뷰(dangling view)가 발생할 수 있습니다.
std::string_view bad() {
std::string s = "hello";
return std::string_view{s}; // dangling after return
}
void ok(std::string_view name) {
// safe only during this call if caller's data outlives the call
}
AI 코치와 함께 이 질문에 답해 보세요