면접 준비

[면접 준비] C++ 항목 정리

지나가던 개발자 2025. 8. 5. 16:30
반응형

✅ 1. 기본 문법 및 언어 구조

🔷포인터와 참조의 차이는 무엇인가?

🔹 모범 답변

  • 포인터는 주소값을 저장하며 *로 접근합니다.
  • 참조는 기존 변수의 별칭으로 동작하며, null이나 재바인딩이 불가능합니다.
  • 포인터는 동적 할당과 배열 탐색에 유리하고, 참조는 함수 인자 전달 등에 많이 사용됩니다.

🔸 심화 질문

  • 참조로 구현할 수 없는 상황은?
  • 참조로 void* 전달 가능한가?
  • T*&와 같은 참조형 포인터는 어떤 용도로 쓰이나요?

심화 답변

  • 참조는 null이나 재할당이 불가능하므로 동적 재연결이 필요한 상황은 포인터를 써야 함
  • void*는 타입 정보가 없으므로 참조로 전달 불가
  • T*&는 포인터 자체를 참조하므로, 호출자에게 포인터를 변경해서 전달할 수 있음

🔷const 키워드의 용도와 위치에 따른 의미 차이를 설명해주세요.

 
1
2
3
4
5
const int* p; // 포인터가 가리키는 값은 상수 (읽기 전용)
 
int* const p; // 포인터 자체는 상수 (다른 주소 못 가리킴)
 
const int* const p; // 포인터도, 값도 상수
cs
cs

🔹 모범 답변

  • const int* p: 값은 고정, 포인터는 변경 가능
  • int* const p: 포인터는 고정, 값은 변경 가능
  • const는 함수 매개변수, 리턴 타입, 클래스 멤버 함수 등 여러 위치에서 사용됨

🔸 심화 질문

  • const 멤버 함수와 mutable 멤버 변수의 관계는?
  • const_cast는 언제 사용되며 위험성은?

심화 답변

  • const 멤버 함수는 멤버를 수정하지 못하지만, mutable 변수는 예외
  • const_cast는 const 속성을 강제로 제거하지만, 진짜 const 객체에서 쓰면 undefined behavior 발생 가능

🔷 static 키워드는 어디에 사용할 수 있고 각각 어떤 의미인가요?

🔹 모범 답변

  • 함수 내 static: 정적 지역 변수
  • 클래스 내 static: 인스턴스 없이 공유
  • 전역에서 static: 내부 연결 (translation unit 제한)

🔸 심화 질문

  • static 변수의 초기화 순서 문제란?
  • static 멤버와 싱글턴과의 차이?

심화 답변

  • 서로 다른 static 객체 간의 초기화 순서가 undefined일 수 있음 (static initialization order fiasco)
  • static 멤버는 단일 값 공유, 싱글턴은 객체 자체를 관리함 (보통 캡슐화와 lazy init 포함)

🔷 C++에서 함수 오버로딩과 오버라이딩의 차이는 무엇인가요?

🔹 모범 답변

  • 오버로딩은 같은 이름의 함수가 매개변수 시그니처만 다른 경우
  • 오버라이딩은 상속 관계에서 virtual 함수를 재정의하는 것
  • 오버라이딩은 런타임 다형성, 오버로딩은 컴파일 타임 분기

🔸 심화 질문

  • 함수 오버로딩이 모호해지는 경우는?
  • 오버라이딩에서 override, final의 역할은?

심화 답변

  • 암시적 변환이 가능한 경우 오버로딩 충돌 발생 가능
  • override는 오타 방지, final은 하위 클래스에서 오버라이딩 방지

🔷 객체 생성 시 생성자 호출 순서는 어떻게 되나요? (상속 포함)

🔹 모범 답변

  • 기본 → 멤버 → 파생 클래스
  • 파생 클래스 생성 시, 기반 클래스가 먼저 호출됨
  • 멤버 변수는 선언 순서대로 생성됨

🔸 심화 질문

  • 상속된 클래스에 가상 함수가 있다면 생성자에서 호출 가능한가?
  • 다중 상속 시 생성 순서는?

심화 답변

  • 생성자 내에서는 해당 클래스의 vtable이 완성되지 않아 가상 함수 호출 위험
  • 다중 상속은 선언 순서에 따라 부모 생성자 호출, 가상 상속이 있는 경우 virtual base는 한 번만 호출됨

🔷 C++에서 컴파일과 링크 과정은 어떻게 이루어지나요?

🔹 모범 답변

  1. Preprocessing (전처리)
  2. Compilation (각 소스 파일을 obj로 컴파일)
  3. Linking (외부 참조 결합)
  4. Executable 생성
  • 컴파일 타임 오류는 각 파일 내부에서 발생, 링크 오류는 외부 참조 불일치 시 발생

🔸 심화 질문

  • ODR(One Definition Rule)이란?
  • inline 함수는 여러 번 정의될 수 있는데 ODR을 위반하지 않는 이유는?

심화 답변

  • ODR: 동일한 기호는 한 번만 정의되어야 한다는 규칙
  • inline 함수는 동일한 내용이라면 컴파일러가 허용 (기계어로 같다면 ok)

🔷가상 함수와 다형성

🔹 모범 답안:

가상 함수는 virtual 키워드로 선언되어 파생 클래스에서 재정의 가능하며, 런타임에 바인딩됩니다. 이를 통해 객체를 기반으로 동작이 결정되는 다형성이 구현됩니다.

1
2
3
4
5
6
7
8
9
class Base 
publicvirtual void Print() { std::cout << "Base\n"; } 
}; 
 
class Derived : public Base 
publicvoid Print() override { std::cout << "Derived\n"; } 
};
cs
  • 포인터나 참조를 통해 Base* 형으로 접근해도 실제 객체의 Print()가 호출됨
  • vtable을 통해 동작하며, 최소 1개의 가상 함수가 있으면 vptr이 생성됨

🔸 심화 질문:

  • virtual 소멸자를 선언하지 않았을 때 발생할 수 있는 문제는?
  • vtable은 클래스마다 하나인가 객체마다 하나인가?
  • final, override 키워드의 의미와 쓰임은?

✅ 2. 메모리 및 자원 관리

🔷 new와 malloc의 차이점은 무엇인가요?

🔹 모범 답변

  • new는 C++ 연산자이며, 생성자 호출과 타입 안전한 메모리 할당을 동시에 수행합니다.
  • malloc은 C 함수이며, 단순한 메모리 블록을 할당하며 생성자 호출이 이루어지지 않습니다.
  • new는 실패 시 예외를 throw, malloc은 nullptr을 반환합니다.

🔸 심화 질문

  • new와 malloc을 혼용할 경우 어떤 문제가 생기나요?
  • operator new와 new 연산자의 차이는?

심화 답변

  • new로 할당 후 free() 호출 시 소멸자가 호출되지 않아 리소스 누수
  • operator new는 메모리 할당 함수(함수 호출), new는 객체 생성까지 포함한 연산자

🔷 스마트 포인터(unique_ptr, shared_ptr, weak_ptr)의 동작 방식과 용도

🔹 모범 답변

  • unique_ptr: 소유권이 하나, 이동 가능, 복사 불가
  • shared_ptr: 참조 카운트 기반 공유, 마지막 하나가 파괴되면 자원 해제
  • weak_ptr: shared_ptr의 순환 참조 방지용, 참조 카운트에 영향을 주지 않음

🔸 심화 질문

  • shared_ptr의 참조 카운트는 어디 저장되며 언제 해제되나요?
  • weak_ptr을 사용할 때 expired()와 lock()의 차이는?

심화 답변

  • 별도의 컨트롤 블록에 참조 카운트와 weak 카운트가 함께 저장됨
  • lock()은 유효할 때만 shared_ptr 반환, expired()는 만료 여부 확인

🔷 RAII란 무엇이며, 왜 중요한가요?

🔹 모범 답변

  • RAII(Resource Acquisition Is Initialization): 자원은 객체 생성과 동시에 확보되고, 소멸 시 자동 해제됨
  • 예: std::lock_guard, std::unique_ptr, fstream 등
  • 예외 안전성과 자원 누수 방지에 매우 효과적

🔸 심화 질문

  • RAII가 멀티스레딩에서 중요한 이유는?
  • RAII를 위반했을 때 발생할 수 있는 상황은?

심화 답변

  • lock을 획득한 후 예외 발생 시 unlock이 자동으로 되지 않으면 데드락 발생 → RAII로 방지 가능
  • 수동 delete나 unlock은 누락되기 쉬워 자원 누수나 교착 상태 초래 가능

🔷 shallow copy와 deep copy의 차이는 무엇인가요?

🔹 모범 답변

  • shallow copy는 단순히 포인터 값만 복사
  • deep copy는 포인터가 가리키는 대상도 새로 복사
  • 포인터 멤버가 있다면 deep copy를 구현해 이중 해제(double free) 방지 필요

🔸 심화 질문

  • Rule of Three/Five와 deep copy의 관계는?
  • move constructor는 shallow copy일까요?

심화 답변

  • deep copy가 필요한 경우, 복사 생성자/대입 연산자/소멸자 모두 정의해야 하므로 Rule of Three가 성립됨
  • move는 자원 이동으로 shallow와 다르며, 기존 객체의 자원을 빼앗는 개념 (소유권 이전)

🔷 메모리 누수(memory leak)는 어떻게 탐지하고 방지할 수 있나요?

🔹 모범 답변

  • delete 누락, 스마트 포인터 미사용, 예외 시 경로 누락 등에서 발생
  • 해결법: RAII, 스마트 포인터, valgrind, CRT Debug Heap, AddressSanitizer

🔸 심화 질문

  • STL 컨테이너에서 메모리 누수가 발생할 수 있나요?
  • 메모리 누수와 dangling pointer의 차이는?

심화 답변

  • vector<T*>에서 내부 포인터를 delete하지 않으면 누수 발생
  • memory leak은 해제 안된 메모리, dangling pointer는 해제된 후 접근한 포인터

✅ 3. 객체 지향 (OOP)

🔷 다형성이란 무엇이며, C++에서 어떻게 구현하나요?

🔹 모범 답변

다형성은 동일한 인터페이스를 통해 다양한 동작을 수행할 수 있는 성질입니다.
C++에서는 virtual 키워드를 이용한 가상 함수와 상속을 통해 런타임 다형성을 구현할 수 있습니다.

🔸 심화 질문

  • 정적 다형성과 동적 다형성의 차이는?
  • C++에서 다형성을 컴파일 타임에 구현하는 방법은?

심화 답변

  • 정적 다형성은 템플릿이나 함수 오버로딩을 통한 컴파일 타임 분기, 동적은 가상 함수 기반 런타임 분기
  • 정적 다형성은 인라인화가 가능해 성능상 유리함, 예: CRTP(Curiously Recurring Template Pattern)

🔷 가상 함수 테이블(vtable)의 구조와 작동 원리는?

🔹 모범 답변

  • 가상 함수가 있는 클래스는 컴파일러가 **vtable(가상 함수 테이블)**을 생성합니다.
  • 객체는 내부에 **vptr(가상 함수 테이블 포인터)**를 포함하며, 이 포인터를 통해 실제 구현을 런타임에 호출합니다.
  • 파생 클래스가 함수를 오버라이딩하면, 해당 슬롯의 함수 포인터가 대체됩니다.

🔸 심화 질문

  • 생성자/소멸자에서 가상 함수를 호출하면 어떤 문제가 생기나요?
  • 다중 상속 시 vtable은 어떻게 관리되나요?

심화 답변

  • 생성자/소멸자에서는 아직 vptr이 파생 클래스용으로 설정되지 않기 때문에, 기반 클래스의 함수만 호출됨
  • 다중 상속 시 각 부모 클래스별로 별도의 vptr/vtable이 존재할 수 있음 (컴파일러에 따라 다름)

🔷 순수 가상 함수와 추상 클래스란?

🔹 모범 답변

  • 순수 가상 함수는 구현이 없고 = 0으로 선언되며, 자식 클래스가 반드시 재정의해야 합니다.
  • 순수 가상 함수를 1개 이상 포함한 클래스는 추상 클래스로 간주되며, 객체를 직접 생성할 수 없습니다.

🔸 심화 질문

  • 추상 클래스에 생성자/소멸자를 정의할 수 있을까요?
  • 인터페이스로서 추상 클래스를 설계할 때 주의할 점은?

심화 답변

  • 생성자/소멸자 정의 가능하며, 보통 소멸자는 virtual로 선언해 다형성 소멸 보장
  • 다중 상속을 고려할 경우 인터페이스는 상태(state)를 가지지 않도록 구성하는 것이 좋음

🔷 상속 시 생성자/소멸자 호출 순서는?

🔹 모범 답변

  • 생성자: 기반 클래스 → 멤버 변수 → 파생 클래스 순으로 호출
  • 소멸자: 파생 클래스 → 멤버 변수 → 기반 클래스 순으로 역순 호출
  • 가상 소멸자가 없으면 파생 클래스의 소멸자가 호출되지 않아 자원 누수 발생 가능

🔸 심화 질문

  • 다형성을 사용하는 클래스는 항상 virtual 소멸자가 필요할까요?
  • 멤버 초기화 리스트와 생성자 본문의 차이는?

심화 답변

  • 가상 함수가 있는 클래스는 virtual ~Class()를 반드시 선언해야 하며, 그렇지 않으면 delete 시 누락 위험
  • 멤버 초기화 리스트는 생성자 호출 전에 실행, 본문은 그 이후 → 초기화가 아닌 대입이 될 수 있어 비효율적

🔷 다중 상속의 문제점과 해결 방법은?

🔹 모범 답변

  • 이름 충돌(name hiding), 모호성(ambiguity), 복잡한 메모리 레이아웃 등이 문제
  • 공통 기반 클래스가 중복 상속될 경우 diamond 문제(다이아몬드 상속) 발생
  • 해결책: virtual 키워드를 사용하여 가상 상속(virtual inheritance) 적용

🔸 심화 질문

  • 가상 상속 시 메모리 배치에 어떤 변화가 생기나요?
  • 다중 상속을 피하기 위해 어떤 설계 방식이 권장되나요?

심화 답변

  • 가상 상속에서는 vtable뿐 아니라, 공통 기반 클래스의 오프셋 정보를 추적해야 하므로 메모리 배치가 더 복잡해짐
  • **구성(composition)**이나 인터페이스 기반 상속을 통해 다중 상속을 피하는 것이 유지보수와 안정성 측면에서 유리

✅ 4. 템플릿 & 제네릭 프로그래밍

🔷 함수 템플릿과 클래스 템플릿의 차이점은?

🔹 모범 답변

  • 함수 템플릿은 함수 호출 시 타입을 자동 추론하여 템플릿 인스턴스 생성
  • 클래스 템플릿은 인스턴스를 생성할 때 타입을 명시적으로 지정해야 함
  • 함수 템플릿은 코드 재사용에 유리하며 STL의 핵심 구현 방식임

🔸 심화 질문

  • 함수 템플릿의 오버로딩과 특수화는 어떻게 다르나요?
  • 클래스 템플릿의 static 멤버는 타입별로 몇 개 생성되나요?

심화 답변

  • 오버로딩은 시그니처에 따른 분기, 특수화는 특정 타입 전용 구현
  • 클래스 템플릿의 static 멤버는 타입마다 각각 별도로 존재

🔷 템플릿 인스턴스화 시점은 언제인가요?

🔹 모범 답변

템플릿은 정의가 필요한 시점에 인스턴스화됩니다.
즉, 실제 사용 시점에서 컴파일러가 타입을 바탕으로 코드를 생성합니다.

🔸 심화 질문

  • 선언만 해놓고 정의하지 않은 템플릿을 사용할 수 있나요?
  • 템플릿 정의는 헤더에 두어야 하는 이유는?

심화 답변

  • 정의가 없다면 링크 오류 발생
  • 컴파일러가 인스턴스화를 위해 템플릿 정의가 필요하므로 헤더에 정의를 포함해야 함

🔷 SFINAE와 enable_if는 어떤 상황에서 쓰이나요?

🔹 모범 답변

SFINAE(Substitution Failure Is Not An Error)는 템플릿 인자 대체 실패를 오류로 처리하지 않고 다른 오버로드로 대체하는 메커니즘입니다.

std::enable_if를 통해 특정 조건에 따라 함수/클래스 템플릿의 활성화를 제어할 수 있습니다.

🔸 심화 질문

  • SFINAE가 필요한 이유는?
  • C++20에서는 어떤 문법이 SFINAE를 대체하나요?

심화 답변

  • 여러 함수 중 적합한 템플릿만 활성화시켜 컴파일 오류 방지 및 유연성 확보
  • C++20에서는 requires, concept을 통해 코드 가독성과 타입 제약 표현이 훨씬 명확해짐

🔷 C++20의 concept는 무엇인가요?

🔹 모범 답변

concept는 템플릿 인자의 타입 요구조건을 명확하게 표현하는 문법입니다.
기존 SFINAE보다 가독성 좋고 에러 메시지가 간결합니다.

🔸 심화 질문

  • concept의 내부 동작은 SFINAE와 동일한가요?
  • concept와 requires 절의 차이는?

심화 답변

  • concept도 내부적으로는 SFINAE와 유사하게 작동하지만 문법 수준에서 직접 지원
  • concept는 템플릿 정의 외부/내부에서 쓸 수 있고, requires는 조건을 정의하는 표현식

✅ 5. STL (표준 템플릿 라이브러리)

✅ STL 컨테이너 비교 - 핵심 개념 요약

컨테이너 내부 구조 검색 속도 삽입/삭제 정렬 유지 사용 예

vector 동적 배열 O(n) 끝: O(1), 중간: O(n) 순차적 처리, 캐시 효율
list 이중 연결 리스트 O(n) O(1) (노드 위치 알고 있을 때) 빈번한 삽입/삭제
deque 분할 배열 O(n) 앞뒤 O(1) double-ended queue
set / map Red-Black Tree O(log n) O(log n) 정렬 필요 시
unordered_set / unordered_map Hash Table 평균 O(1), 최악 O(n) O(1) 빠른 탐색 목적

🔷 vector vs list vs deque의 내부 구조 및 특징은?

🔹 모범 답변

  • vector: 연속 메모리, 빠른 접근, 캐시 효율 우수
  • list: 이중 연결 리스트, 삽입/삭제 빠름, 캐시 비효율
  • deque: 분할 배열 구조, 앞뒤 삽입 빠름, 중간 삽입/삭제 비효율

🔸 심화 질문

  • vector의 capacity 관리 방식은?
  • deque의 내부 블록 구조는?

심화 답변

  • vector는 2배 방식으로 capacity를 재할당하며 재할당 시 모든 요소 복사 발생
  • deque는 고정 크기 블록 배열을 포인터 배열로 관리해 앞뒤 삽입을 빠르게 처리

🔷 map과 unordered_map의 차이점은?

🔹 모범 답변

  • map: Red-Black Tree 기반, O(log n), 정렬된 순서
  • unordered_map: 해시 기반, 평균 O(1), 순서 없음

🔸 심화 질문

  • unordered_map에서 충돌이 많이 발생하면?
  • 커스텀 타입을 unordered_map의 key로 사용하려면?

심화 답변

  • 충돌이 많으면 체이닝 길이가 늘어나 성능이 O(n)까지 악화됨
  • operator==와 std::hash의 특수화를 제공해야 함

🔷 set, multiset의 차이점은?

🔹 모범 답변

  • set: 중복 허용 안 함, 자동 정렬
  • multiset: 중복 허용, 동일 키 여러 개 저장 가능

🔸 심화 질문

  • multiset은 어떤 상황에서 유리한가요?
  • map과 set의 내부 차이는?

심화 답변

  • 동일 값을 중복 저장해야 할 때 유용 (ex: 이벤트 타임스탬프 기록 등)
  • map은 key-value 쌍 저장, set은 key 자체가 value

🔷 lower_bound, upper_bound는 어떻게 동작하나요?

🔹 모범 답변

  • lower_bound: 이상(≥) 인 첫 위치
  • upper_bound: 초과(>) 인 첫 위치
  • 둘 다 정렬된 컨테이너에서 이진 탐색 기반으로 동작

🔸 심화 질문

  • unordered_map에서 lower_bound가 없는 이유는?

심화 답변

  • unordered 계열은 해시 기반이라 순서 정보가 없기 때문

🔷 iterator의 종류와 차이점은?

🔹 모범 답변

  • Input, Output, Forward, Bidirectional, RandomAccess
  • vector는 RandomAccess, list는 Bidirectional

🔸 심화 질문

  • iterator category가 성능에 미치는 영향은?

심화 답변

  • 반복자 category는 알고리즘 최적화(예: std::advance)에서 최적 루프 선택 기준이 됨

🔷vector와 list의 차이는 무엇인가요?

🔹 모범 답안:

  • vector는 동적 배열 기반으로 **임의 접근(O(1))**이 빠르고, 캐시 효율성이 좋습니다.
  • list는 이중 연결 리스트로, **삽입/삭제 O(1)**이 가능하지만 임의 접근은 느립니다(O(n)).
  • vector는 메모리가 연속적이고, list는 노드가 산개되어 있어 캐시 성능이 떨어질 수 있습니다.

게임 클라이언트에서는 대량의 객체를 순회하며 업데이트하거나 렌더링할 때 vector가 더 적합합니다.

🔸 심화 질문:

  • vector에서 중간 삽입이 빈번하다면 어떤 문제가 발생하나요?
  • list가 필요 이상으로 느려지는 실제 사례를 설명해보세요.
  • C++20의 std::span이 vector와 어떤 차이를 가지나요?

🔷map과 unordered_map의 차이는?

🔹 모범 답안:

  • map은 Red-Black Tree 기반으로 항상 정렬된 상태 유지, 삽입/탐색/삭제가 O(log n).
  • unordered_map은 Hash Table 기반으로 정렬 X, 평균 탐색/삽입 O(1), 충돌 발생 시 최악 O(n).
  • unordered_map은 key가 정렬을 요구하지 않는 경우 빠른 검색 성능을 보입니다.

예를 들어, 게임 내 유저 ID에 빠르게 접근할 땐 unordered_map, 순서가 중요하면 map 사용

🔸 심화 질문:

  • hash 충돌이 많이 발생하면 어떤 일이 생기나요? 어떻게 방지하나요?
  • unordered_map을 사용할 때 key 타입에 어떤 제약이 있나요?
  • map이 operator<를 요구하는 이유는?

🔷vector vs deque

🔹 모범 답안:

  • vector는 **push_back()**은 O(1)이지만 **push_front()**는 O(n)
  • deque는 앞뒤 모두 O(1) 삽입/삭제 가능
  • 내부적으로는 분할된 블록 구조로 되어 있어 메모리 연속성이 떨어짐 → vector보다 캐시 효율이 낮을 수 있음

게임 클라이언트에서 이벤트 로그를 앞뒤로 추가/삭제할 경우 deque가 적합

🔸 심화 질문:

  • deque는 어떤 구조로 앞뒤 삽입을 빠르게 지원하나요?
  • 메모리 재할당 비용은 deque가 더 낮은가요?
  • deque가 사용되는 실제 사례는?

🔷 set과 unordered_set의 차이

🔹 모범 답안:

  • set: 정렬된 원소 저장, 중복 불가, 삽입/탐색 O(log n)
  • unordered_set: 정렬 없음, 해시 기반, 탐색 평균 O(1), 충돌 시 성능 저하
  • set은 operator< 필요, unordered_set은 hash 함수 필요

🔸 심화 질문:

  • set을 사용할 때 커스텀 비교자를 지정하려면 어떻게 하나요?
  • unordered_set에서 커스텀 객체를 key로 쓰려면 어떤 연산자/함수가 필요한가요?
  • 멀티스레드 환경에서 unordered_set을 사용할 때 고려할 점은?

🔷 lower_bound, upper_bound는 어떤 함수인가요?

🔹 모범 답안:

  • lower_bound: **이상(≥)**인 첫 번째 요소의 iterator 반환
  • upper_bound: **초과(>)**인 첫 번째 요소의 iterator 반환
  • set, map, vector + binary_search에 유용하게 사용됨
  • 정렬된 컨테이너에서만 동작하며, 이진 탐색 기반으로 동작

🔸 심화 질문:

  • lower_bound를 unordered_map에서 쓸 수 없는 이유는?
  • map<int, vector> 구조에서 특정 범위 검색 시 lower_bound를 어떻게 활용하나요?
  • equal_range()는 어떤 상황에서 유용한가요?

🔷STL 컨테이너 성능을 게임에 어떻게 적용하나요?

🔹 모범 답안:

  • vector: 프레임마다 매번 순회하며 업데이트하는 유닛 리스트 등에 사용
  • unordered_map: 유저 ID → 정보 매핑, 리소스 테이블 관리
  • set: 타겟팅 우선순위, 정렬된 상태 유지가 필요한 경우
  • deque: 커맨드 큐, 히스토리 로그
  • list: 오브젝트 풀링에서 연결 리스트로 사용되는 경우 (주의: 대부분 vector로 대체 가능)

✅ 정리 요약

  • 탐색 속도 필요 → unordered_map
  • 정렬된 상태 유지 → map, set
  • 빈번한 삽입/삭제 → list (대부분 vector로 커버 가능)
  • 연속적인 순차처리 + 캐시 효율 → vector
  • 앞뒤 삽입이 필요 → deque

✅ 6. 성능 및 최적화

🔷 inline 함수와 매크로의 차이점은?

🔹 모범 답변

  • inline 함수는 타입 안전 + 디버깅 가능 + 함수처럼 동작
  • 매크로는 단순 치환, 타입 체크 불가, 디버깅 어려움

🔸 심화 질문

  • inline 함수가 항상 인라인되나요?

심화 답변

  • 아닙니다. 컴파일러는 최적화 판단에 따라 인라인 여부를 결정하며, __forceinline은 힌트일 뿐 보장되지 않음

🔷 캐시 미스와 false sharing이란?

🔹 모범 답변

  • 캐시 미스: 필요한 데이터가 캐시에 없어 메모리 접근이 발생
  • false sharing: 서로 다른 쓰레드가 같은 캐시 라인의 다른 데이터를 쓰며 성능 저하

🔸 심화 질문

  • false sharing을 방지하는 방법은?

심화 답변

  • 쓰레드별 데이터 사이에 padding 또는 alignas(64)를 삽입해 캐시 라인 분리

🔷 memory pool은 언제 쓰이고 어떤 장점이 있나요?

🔹 모범 답변

  • 자주 생성/삭제되는 객체를 미리 할당해두고 재사용
  • new/delete 비용 감소, 메모리 단편화 방지, 캐시 적중률 향상

🔸 심화 질문

  • STL 컨테이너에 custom allocator를 적용할 수 있나요?

심화 답변

  • 가능함. std::vector<T, MyAllocator> 형태로 정의해 메모리 풀과 연동 가능

🔷 게임 클라이언트에서 object pooling이 필요한 이유는?

🔹 모범 답변

  • 게임에서는 총알, 이펙트, NPC 등 객체 생성/삭제가 빈번
  • 오브젝트 풀을 사용하면 생성 비용과 GC 오버헤드를 줄이고 성능을 안정화시킬 수 있음

🔸 심화 질문

  • 오브젝트 풀에서 상태 초기화는 누가 책임지나요?

심화 답변

  • 보통 객체가 반환될 때 초기화 함수를 호출하거나, 사용할 때 Reset()을 호출함 → 책임 분리 중요

🔷 move semantics와 rvalue reference는 어떻게 활용하나요?

🔹 모범 답변

  • 값의 복사 비용이 큰 객체에서 std::move를 사용해 자원의 소유권 이전
  • 임시 객체(rvalue)에 대해 복사 대신 이동을 수행

🔸 심화 질문

  • std::move가 정말로 객체를 이동시키나요?

심화 답변

  • 아니요. 단순히 rvalue로 변환하는 캐스팅 함수입니다. 실제 이동은 이동 생성자/대입 연산자가 수행

✅ 7. 멀티스레딩 및 동기화

🔷 C++11의 std::thread는 어떻게 사용하나요?

🔹 모범 답변

std::thread는 C++11에서 도입된 표준 스레드 클래스입니다.
함수, 람다, functor 등을 인자로 받아 스레드를 생성합니다.

🔸 심화 질문

  • join()과 detach()의 차이는?
  • 스레드 객체를 이동시킬 수 있나요?

심화 답변

  • join()은 해당 스레드가 종료될 때까지 대기, detach()는 백그라운드로 분리
  • std::thread는 복사 불가, 이동 가능 (std::move 필요)

🔷 mutex, lock_guard, unique_lock의 차이점은?

🔹 모범 답변

  • std::mutex: 수동 lock()/unlock() 필요
  • lock_guard: 생성 시 락, 소멸 시 자동 해제 (RAII)
  • unique_lock: 조건 변수와 함께 사용 가능하며 더 유연함

🔸 심화 질문

  • scoped_lock은 어떤 상황에서 사용되나요?
  • try_lock()은 언제 유용한가요?
  • unique_lock이 lock_guard보다 무거운 이유는?
  • 데드락은 어떤 상황에서 발생하고 이를 어떻게 방지하나요?

심화 답변

  • scoped_lock은 여러 mutex를 한 번에 안전하게 잠글 때 사용 (deadlock 방지)
  • try_lock()은 즉시 락을 얻을 수 없을 경우 다른 작업을 수행할 수 있어 유용
  • unique_lock은 lock/unlock을 수동으로 제어 가능하므로 내부 상태를 더 많이 저장함
  • 순서 없이 여러 락을 획득하거나 락 획득 후 블로킹 호출 시 데드락 발생 → 락 획득 순서 고정, try_lock, timeout 전략 사용

🔷 데드락(deadlock)이 발생하는 상황과 해결 방법은?

🔹 모범 답변

데드락은 두 개 이상의 스레드가 서로가 가진 자원을 기다릴 때 발생합니다.
예: A가 B의 mutex를, B가 A의 mutex를 기다릴 때

해결 방법:

  • 락 획득 순서 고정
  • std::lock() 사용
  • timeout 기반 락 시도 (try_lock_for)

🔸 심화 질문

  • 교착 상태가 반드시 발생하는 네 가지 조건은?

심화 답변

  1. 상호 배제
  2. 점유와 대기
  3. 비선점
  4. 순환 대기
    → 이 조건 중 하나라도 제거하면 데드락 방지 가능

🔷 condition_variable은 언제 사용하나요?

🔹 모범 답변

  • 쓰레드 간 이벤트 기반 통신이 필요할 때 사용
  • 생산자-소비자 패턴 구현에 필수
  • wait()은 unique_lock과 함께 사용되며, 조건이 만족될 때까지 대기

🔸 심화 질문

  • spurious wakeup이란 무엇인가요?

심화 답변

  • 조건이 false인데도 wait()가 예기치 않게 깨는 현상
  • 이를 방지하기 위해 wait(lock, condition) 형식으로 사용해야 함

🔷 멀티스레딩 환경에서 게임 데이터를 안전하게 다루려면?

🔹 모범 답변

  • 쓰레드 간 공유 데이터 접근 시 반드시 **동기화 도구(mutex, atomic 등)**를 사용해야 함
  • lock-free 구조가 가능하다면 atomic이나 ring buffer 기반 구조 활용 권장

🔸 심화 질문

  • lock-free queue는 어떻게 작동하나요?

심화 답변

  • CAS(compare-and-swap) 기반으로 경쟁 없이 데이터 삽입/삭제
  • 다중 생산자/소비자에서는 메모리 순서와 캐시 일관성에 주의 필요

✅ 8. 게임 클라이언트 특화 C++ 질문

🔷 게임 루프는 어떻게 구성되며 어떤 방식으로 최적화하나요?

🔹 모범 답변

기본 루프는 다음과 같은 흐름으로 구성됩니다:

while (running) {

ProcessInput();

Update(deltaTime);

Render();

}

  • 고정 timestep과 가변 timestep을 분리해 처리 (physics vs logic)
  • 최적화 포인트: deltaTime 관리, 렌더링 최적화, 이벤트 배치

🔸 심화 질문

  • Fixed timestep vs variable timestep의 차이는?

심화 답변

  • fixed는 안정적인 물리 시뮬레이션, variable은 입력 반응성 증가
  • 둘을 병행하려면 accumulator 패턴 활용

🔷 상태 머신(FSM)은 게임에서 어떻게 구현하나요?

🔹 모범 답변

FSM은 상태(state)와 상태 간 전이(transition)를 코드로 명시하여,
플레이어/몬스터 AI, UI 흐름 등에 사용됩니다.

cpp

복사편집

enum class State { Idle, Attack, Die };

🔸 심화 질문

  • FSM이 복잡해질 때 어떻게 확장하나요?

심화 답변

  • 상태 클래스 추상화(State Pattern), 또는 Behavior Tree 등 고차원 설계로 확장

🔷 메시지 큐 시스템을 C++로 어떻게 설계하나요?

🔹 모범 답변

  • 메시지 구조체(Message { Type, Payload })를 정의
  • Queue에 삽입하고, 시스템 간 독립적으로 처리
  • 이벤트 디커플링, 비동기 처리에 유리

🔸 심화 질문

  • 메시지 큐가 병목이 될 수 있는 상황은?

심화 답변

  • lock 기반 큐 사용 시 동기화 병목 발생 → lock-free 구조나 double buffering으로 개선

🔷 컴포넌트 기반 구조(Entity-Component System)는 어떻게 구현하나요?

🔹 모범 답변

  • Entity: ID만 갖는 단순 객체
  • Component: 데이터만 보유
  • System: 로직만 수행
  • 데이터 중심 구조로 캐시 효율과 병렬성 확보

🔸 심화 질문

  • OOP보다 ECS가 성능 면에서 유리한 이유는?

심화 답변

  • SoA(Structure of Arrays) 방식으로 구성 → 대량의 객체에 대해 연산 시 캐시 적중률 극대화
  • 데이터 중심 접근 덕분에 SIMD, 병렬화 용이

🔷 실시간 네트워크 패킷을 안전하게 처리하려면?

🔹 모범 답변

  • 수신 버퍼 → 패킷 파싱 → 메시지 큐에 삽입
  • 버퍼 오버플로, 비동기 경합, 패킷 순서 문제를 모두 고려해야 함

🔸 심화 질문

  • TCP/UDP 각각 어떤 데이터에 적합한가요?

심화 답변

  • TCP: 채팅, 인벤토리 등 신뢰성 요구 데이터
  • UDP: 위치 동기화, 조작 명령 등 빠른 전송이 우선인 경우

✅ 9. 기타 주제

🔷 volatile 키워드의 의미는?

🔹 모범 답변

  • 변수 값이 외부 요인(하드웨어 등)에 의해 변경될 수 있으므로 최적화 제외 요청
  • 멀티스레드 메모리 동기화 보장 수단은 아님

🔸 심화 질문

  • volatile이 멀티스레드에서 동기화 보장 안 되는 이유?

심화 답변

  • CPU 캐시, compiler reorder 등의 문제로 메모리 장벽(memory barrier) 없이 일관성 보장 안 됨
  • 대신 std::atomic 사용 필요

🔷 undefined behavior의 예시와 방지 방법은?

🔹 모범 답변

  • ex) 초기화되지 않은 변수 사용, 배열 범위 초과 접근, double delete 등
  • 컴파일러 최적화 결과 예측 불가 → 프로그램이 정상 작동하지 않음

🔸 심화 질문

  • UB가 발생해도 잘 실행되는 것처럼 보이는 이유는?

심화 답변

  • 컴파일러가 최적화를 공격적으로 수행하며, UB 상황에서는 어떤 동작도 보장되지 않음

🔷 함수 포인터와 람다 함수의 차이는?

🔹 모범 답변

  • 함수 포인터: 단순 주소 참조
  • 람다: 상태 캡처 가능, operator() 오버로드
  • 람다는 std::function과 함께 사용 가능

🔸 심화 질문

  • 캡처된 변수는 언제까지 유지되나요?

심화 답변

  • 람다 객체 수명이 끝날 때까지 유지됨 → reference 캡처 주의 필요

🔷 RTTI(Runtime Type Identification)는 어떻게 작동하나요?

🔹 모범 답변

  • typeid, dynamic_cast 등을 통해 런타임에 실제 타입 확인
  • RTTI를 위해 클래스에 가상 함수가 있어야 함

🔸 심화 질문

  • RTTI가 비활성화된 빌드에서 dynamic_cast는 어떻게 동작하나요?

심화 답변

  • 실패하거나 프로그램이 크래시 날 수 있음 → RTTI 사용 여부 컴파일 옵션 확인 필요

🔷 커스텀 할당자(Custom Allocator)는 언제 사용하나요?

🔹 모범 답변

  • 메모리 풀, 오브젝트 풀 등을 구현하여 할당 정책 최적화
  • STL 컨테이너에 삽입해 할당 전략 통제 가능

🔸 심화 질문

  • STL 컨테이너에서 커스텀 할당자 삽입 방식은?

심화 답변

  • std::vector<T, MyAllocator> 형태로 정의
  • allocator는 allocate, deallocate, construct 등 메서드 오버라이드 필요

🔷 함수 템플릿과 클래스 템플릿의 차이점은?

🔹 모범 답안:

  • 함수 템플릿은 함수에 들어오는 매개변수 타입에 따라 자동 추론 가능
  • 클래스 템플릿은 객체 생성 시 명시적으로 타입을 지정해야 함

🔸 심화 질문:

  1. 템플릿 인자가 많아질 경우 디버깅이 어려워지는 이유는?
  2. 함수 템플릿 오버로딩과 특수화는 어떻게 다르며 언제 사용하나요?
  3. 클래스 템플릿에서 static 멤버는 어떻게 관리되나요?

✅ 심화 질문 답변:

  1. 템플릿은 컴파일 타임에 인스턴스화되므로 에러 메시지가 복잡하게 출력됨
  2. 특수화는 특정 타입에 대한 구현, 오버로딩은 함수 시그니처에 따른 일반적인 분기 처리
  3. 클래스 템플릿의 static 멤버는 타입 인자마다 독립적으로 생성

🔷mutex, lock_guard, unique_lock의 차이는?

🔹 모범 답안:

  • std::mutex: 가장 기본적인 락 객체, lock()과 unlock() 필요
  • lock_guard: 생성 시 lock, 소멸 시 unlock (RAII 패턴)
  • unique_lock: lock_guard보다 유연하며, 지연 락/재귀/조건변수 연동 가능

🔸 심화 질문:

  1. unique_lock이 lock_guard보다 무거운 이유는?
  2. std::scoped_lock은 무엇이고 어떤 상황에서 쓰나요?
  3. 데드락은 어떤 상황에서 발생하고 이를 어떻게 방지하나요?

✅ 심화 질문 답변:

  1. unique_lock은 lock/unlock을 수동으로 제어 가능하므로 내부 상태를 더 많이 저장함
  2. scoped_lock은 여러 개의 mutex를 동시에 락할 때 데드락 방지용으로 사용됨
  3. 순서 없이 여러 락을 획득하거나 락 획득 후 블로킹 호출 시 데드락 발생 → 락 획득 순서 고정, try_lock, timeout 전략 사용

🔷Rule of Three, Five, Zero란?

🔹 모범 답안:

  • Rule of Three: 소멸자, 복사 생성자, 복사 대입 연산자 중 하나를 정의하면 나머지도 정의해야 함
  • Rule of Five: C++11 이후, 이동 생성자와 이동 대입 연산자까지 포함
  • Rule of Zero: RAII와 스마트 포인터 등을 활용해 자원 관리를 사용자 정의하지 않도록 설계

🔸 심화 질문:

  1. 이동 생성자와 복사 생성자가 모두 있을 때 호출 우선순위는?
  2. Rule of Five를 지키지 않으면 어떤 문제가 생기나요?
  3. 사용자 정의 소멸자만 정의했을 때 어떤 일이 발생할 수 있나요?

✅ 심화 질문 답변:

  1. rvalue가 전달되면 이동 생성자가, lvalue가 전달되면 복사 생성자가 호출됨
  2. 자원 중복 해제, double delete, move 이후 dangling pointer 등의 문제가 생김
  3. 사용자 정의 소멸자만 정의하고 복사 생성자를 정의하지 않으면 컴파일러가 기본 복사 생성자를 만들지 않아 객체 복사가 막힘

🔷싱글턴 패턴은 어떻게 구현하나요?

🔹 모범 답안:

  • 전역에서 하나의 인스턴스만 존재하도록 보장하는 패턴
  • 게임에서는 리소스 관리자, 설정 관리자, 상태 관리자 등에 사용

🔸 심화 질문:

  1. 싱글턴의 단점과 테스트 측면의 문제는?
  2. 멀티스레딩 환경에서 싱글턴을 안전하게 만들려면?
  3. 싱글턴 패턴은 반드시 필요한가요?

✅ 심화 질문 답변:

  1. 전역 의존성 증가, 테스트 어려움, 수명 관리 어려움 등
  2. C++11의 정적 지역 변수 초기화는 스레드 안전 보장
  3. 대부분의 경우 의존성 주입(DI) 구조나 서비스 로케이터 패턴이 더 나은 선택

✅ 구조체 패딩 관련 면접 질문


🔷 구조체 패딩이란 무엇인가요?

🔹 모범 답변

구조체 패딩은 CPU의 메모리 정렬 규칙에 따라, 구조체 멤버 사이 또는 끝에 빈 공간이 삽입되는 현상입니다.

  • 이유: 대부분의 CPU는 특정 데이터 타입을 **자연스러운 정렬(boundary)**에 맞춰 접근할 때 더 빠릅니다.
  • 정렬 단위를 맞추기 위해 컴파일러가 자동으로 패딩 바이트를 삽입합니다.

🔸 심화 질문

  1. 구조체 패딩이 성능에 어떤 영향을 주나요?
  2. #pragma pack은 어떤 역할을 하나요?
  3. false sharing과 패딩은 어떤 관계가 있나요?
  4. C++20 [[no_unique_address]]와 구조체 크기 최적화는 어떤 관계가 있나요?

심화 질문에 대한 답변


  1. 구조체 패딩이 성능에 어떤 영향을 주나요?
  • 패딩으로 인한 메모리 낭비가 발생하고, 캐시 효율도 떨어질 수 있습니다.
  • 멤버 재배치를 통해 padding을 줄이면 데이터 캐시 적중률이 향상되어 성능이 좋아질 수 있습니다.
  • 예: 수천 개의 객체를 순회할 때 불필요한 패딩으로 인해 L1/L2 캐시에 덜 들어가게 됨

  1. #pragma pack은 어떤 역할을 하나요?
  • 구조체의 **정렬 단위(boundary)**를 지정하여 패딩을 줄이거나 제거합니다.
  • 다만 성능 저하CPU misaligned access 문제가 발생할 수 있으므로 신중히 사용해야 합니다.

  1. false sharing과 패딩은 어떤 관계가 있나요?
  • false sharing은 서로 다른 쓰레드가 동일한 캐시 라인을 공유하며 불필요한 캐시 동기화 비용이 발생하는 현상입니다.
  • 이 문제를 막기 위해 의도적으로 padding을 삽입하여 캐시 라인 경계를 맞추는 경우가 많습니다.

  1. C++20 [[no_unique_address]]와 구조체 크기 최적화는 어떤 관계가 있나요?
  • [[no_unique_address]]는 빈 멤버 객체를 구조체 내에서 패딩 없이 중첩시킬 수 있게 해줍니다.
  • 이를 활용하면 구조체 크기를 줄이고 불필요한 메모리 낭비를 줄일 수 있습니다.

🔚 요약 정리

  • 구조체 패딩은 메모리 정렬 최적화를 위한 필수 과정
  • 멤버 재배치를 통해 줄일 수 있고, #pragma pack으로 강제 제어 가능
  • 게임 클라이언트에서 대량의 구조체 배열을 다룰 경우, 캐시 효율과 메모리 최적화를 위해 패딩 고려는 매우 중요

✅ 객체지향 프로그래밍(OOP)에 대한 일반 질문


🔷 객체지향 프로그래밍이란 무엇인가요?

🔹 모범 답변

객체지향 프로그래밍(OOP, Object-Oriented Programming)은 현실 세계의 사물(Object)을 추상화하여 소프트웨어로 표현하는 프로그래밍 패러다임입니다.
OOP는 다음의 4대 특성을 중심으로 구성됩니다:

  1. 캡슐화(Encapsulation): 내부 구현은 숨기고 인터페이스만 외부에 제공
  2. 상속(Inheritance): 기존 클래스를 기반으로 기능을 확장
  3. 다형성(Polymorphism): 동일한 인터페이스로 다양한 구현 호출 가능
  4. 추상화(Abstraction): 중요한 개념만 표현하고 세부 구현은 감춤

🔸 심화 질문

  1. 캡슐화와 추상화의 차이는 무엇인가요?
  2. OOP의 단점이나 한계는 무엇이 있나요?
  3. 게임 개발에서 OOP 대신 ECS(Entity Component System)를 사용하는 이유는?

심화 답변


  1. 캡슐화와 추상화의 차이는?
  • 캡슐화는 객체 내부 구현을 감추고 외부에 일관된 인터페이스만 제공하는 행위 (데이터 보호 목적)
  • 추상화는 복잡한 시스템에서 핵심 개념만 선택적으로 표현하여 불필요한 세부사항은 감추는 것 (개념 단순화 목적)

요약:

추상화는 무엇을 보여줄 것인가,
캡슐화는 어떻게 숨길 것인가에 집중합니다.


  1. OOP의 단점이나 한계는?
  • 성능 오버헤드: 가상 함수 호출, 메모리 분산 배치로 인해 캐시 비효율 발생
  • 복잡성 증가: 깊은 상속 구조는 디버깅과 유지보수를 어렵게 함
  • 데이터 중심 처리 비효율: 게임 클라이언트에서는 update() 등 동일 연산 반복이 많아 구조 분리 불리

  1. 게임 개발에서 ECS를 사용하는 이유는?
  • OOP는 객체 간 강한 결합가상 함수 호출 비용이 존재
  • ECS는 데이터와 로직을 분리하여 **데이터 중심 구조(SoA, Structure of Arrays)**를 만들고, CPU 캐시 효율을 극대화
  • Update() 호출 비용 감소, SIMD 최적화 가능 등 성능상 이점

✅ 추가적으로 나올 수 있는 응용형 OOP 질문

  • 추상화와 인터페이스의 차이는?
  • C++에서 다형성을 사용하는 경우와 피하는 경우는?
  • 상속보다는 조합(composition)이 더 나은 경우는 언제인가요?
  • OOP에서 SOLID 원칙은 무엇인가요?
반응형