[말랑 퀴즈] 26/10/16 해답

게시:     수정

카테고리:

태그:

제 말랑말랑 퀴즈 생성기는 이곳에서 확인하실 수 있습니다.

말랑말랑 퀴즈 — 해답지 ✅

날짜: 2026-10-16 문제 수: 6문제 총점: 55/60 (92점) 🏆 훌륭해요

📊 채점 결과

문항 유형 난이도 결과 점수
Q1 서술형 🔴 어려움 🔺 부분 정답 7.5 / 10
Q2 객관식 🟢 쉬움 ⭕ 정답 10 / 10
Q3 빈칸 채우기 🟡 보통 🔺 부분 정답 7.5 / 10
Q4 객관식 🟡 보통 ⭕ 정답 10 / 10
Q5 OX 🟢 쉬움 ⭕ 정답 10 / 10
Q6 📌 OX 🟡 보통 ⭕ 정답 10 / 10
합계       55 / 60

총평

객관식 두 문제와 OX 두 문제를 모두 만점으로 풀었고, 오답노트 복습 문제(Q6)도 “라우터는 MAC이 아니라 IP 기준”이라는 핵심을 정확히 짚었습니다. 서술형 Q1은 재귀식 인스턴스화와 특수화의 역할(2번)을 정확히 설명했지만, TMP의 정의와 if문이 컴파일 시점에 모든 분기를 생성한다는 점이 약했습니다. Q3은 복사 생성자가 기본 제공 타입을 “비트” 단위로 복사한다는 디테일을 놓쳤습니다.


Q1. 🔴 어려움 — 🔺 7.5/10

문제: C++ 템플릿 메타프로그래밍(TMP)에 대해 답하시오.

template<unsigned n>
struct Factorial {
	enum { value = n * Factorial<n-1>::value };
};

template<>
struct Factorial<0> {
	enum { value = 1 };
};

int main() {
	cout << Factorial<5>::value;   // 120
}

(1) 템플릿 메타프로그래밍이란 무엇이며, 기존 작업을 런타임에서 컴파일 타임으로 옮기면 얻는 이점 두 가지를 쓰시오.

(2) 위 Factorial에서 TMP에는 반복문(루프)이 없는데 어떻게 반복 효과를 내는가? 그리고 Factorial<0> 특수화가 하는 역할은 무엇인가?

(3) 반복자 종류를 typeid와 일반 if문으로 비교해 분기하는 advance보다, 특성정보를 이용한 오버로딩 방식이 더 나은 이유를 “컴파일”과 “런타임”이라는 표현을 써서 설명하시오.

(4) TMP의 실용적 활용 사례를 두 가지 이상 쓰시오.

📝 내 풀이:

(1) 템플릿 메타프로그래밍이란 템플릿을 통해 객체를 템플릿 인스턴스로 만드는 것이다.

  1. 에러가 컴파일 타임에 발생하여 빠르게 점검할 수 있다. 2. 런타임 시점의 작업을 컴파일 시점으로 옮겨, 실행 속도가 빨라진다.

(2) 재귀식 템플릿 인스턴스화를 사용하여 반복 효과를 낸다. Factorial<0> 특수화는 종료 시점을 보장해준다.

(3) 타입 점검 동작이 컴파일이 아닌 런타임 시점에 발생하여 성능과 안정성이 떨어진다.

(4) 이건 잘몰라서 직접 봤다. 선행 에러 탐지, 행렬 연산 최적화, 맞춤식 디자인 패턴 중 하나인 정책 기반 설계를 구현할 수 있다.

정답:

(1) TMP는 컴파일 도중 실행되는 템플릿 기반의 프로그램을 작성하는 일이다. 기존 작업이 런타임에서 컴파일 타임으로 옮겨지므로, ① 컴파일 도중에 에러를 찾을 수 있고(선행 에러 탐지), ② 실행 코드가 작아지고 실행 시간이 짧아지며 메모리도 적게 사용하는 등 런타임 효율이 높아진다. (대신 컴파일 시간은 길어진다.)

(2) TMP에는 반복(iteration) 의미의 루프가 없고, 재귀식 템플릿 인스턴스화로 루프의 효과를 낸다. Factorial<5>가 Factorial<4>를, 그것이 Factorial<3>을 … 차례로 인스턴스화한다. Factorial<0>은 템플릿 특수화 버전으로 value = 1을 정의해 재귀를 끝내는 종료 조건(기저 사례) 역할을 한다. 이 계산은 런타임이 아니라 컴파일 시간에 이루어진다.

(3) typeid를 쓴 방식은 타입 점검이 컴파일이 아니라 런타임에 일어나며, 런타임 타입 점검 코드가 실행 파일에 들어갈 수밖에 없어 효율이 떨어진다. 또 if문이 런타임에 분기하더라도 컴파일 시점에는 모든 분기의 코드가 생성되므로, list처럼 +=를 지원하지 않는 반복자에서는 iter += d에서 컴파일 에러가 난다. 반면 특성정보를 함수 오버로딩에 쓰면 타입에 따른 선택이 컴파일 도중에 이루어진다.

(4) 다음 중 두 가지 이상: ① 치수 단위(Dimensional Unit)의 정확성 확인(예: 속도 변수에 질량을 대입하는 실수를 컴파일 타임에 잡음) ② 표현식 템플릿을 이용한 행렬 연산 최적화(임시 객체 제거, 루프 합치기) ③ 정책 기반 설계를 통한 맞춤식 디자인 패턴 구현(정책 템플릿을 조합해 생성식 프로그래밍).

🔍 피드백:

  • (1) 1.5/2.5 — 이점 두 가지(컴파일 타임 에러 검출, 실행 속도 향상)는 정확합니다. 그러나 정의는 정확하지 않습니다. TMP는 “객체를 인스턴스로 만드는 것”이 아니라 컴파일 도중 실행되는 템플릿 기반의 프로그램을 작성하는 일입니다.
  • (2) 2.5/2.5 — 재귀식 템플릿 인스턴스화와, Factorial<0> 특수화가 재귀의 종료 조건이라는 점을 정확히 답했습니다.
  • (3) 1/2.5 — “타입 점검이 컴파일이 아닌 런타임에 일어난다”는 핵심 한 가지는 맞습니다. 하지만 if문 분기로는 컴파일 시점에 모든 분기의 코드가 생성되어 +=를 지원하지 않는 반복자에서 컴파일 에러가 난다는 점, 그리고 오버로딩 방식은 타입에 따른 선택이 컴파일 도중에 끝난다는 점이 빠졌습니다.
  • (4) 2.5/2.5 — 행렬 연산 최적화, 정책 기반 설계 두 가지를 정확히 썼습니다. “선행 에러 탐지”는 활용 사례라기보다 이점인데, 이 문제에서는 치수 단위 확인이 그 사례에 해당합니다. 직접 보고 쓰셨다고 하셨지만 0점 처리를 명시하지는 않으셔서 내용 그대로 채점했습니다.

출처: game_dev/cpp/chapter7/2025-11-03-cpp_7_48.md


Q2. 🟢 쉬움 — ⭕ 10/10

문제: UML 다이어그램에 대한 설명으로 가장 올바른 것은?

  • A. 시퀀스 다이어그램에서 객체는 타원으로, 메시지는 네모 상자로 표현한다.
  • B. 액티비티 다이어그램에서는 활동 상태를 타원으로 표현하며, “해커”와 “실제 사용자”처럼 조건에 따라 흐름을 나누는 조건 분기도 쓸 수 있다.
  • C. 시퀀스 다이어그램은 주고받은 메시지의 처리 방식을 자세히 표현하는 데 가장 적합하다.
  • D. 액티비티 다이어그램은 시간 순서에 따른 객체 간 메시지 전달을 표현하는 것이 가장 큰 목적이다.

📝 내 선택: B

정답: B

해설:

액티비티 다이어그램은 활동 상태를 타원 도형으로 표현하고, 조건 분기를 써서 “해커 / 실제 사용자” 같은 경우를 나눌 수 있다.

  • A: 시퀀스 다이어그램에서 객체는 네모 상자, 메시지는 화살표로 표현한다.
  • C: 시퀀스 다이어그램은 주고받은 메시지의 처리 방식을 표현하는 데 한계가 있다.
  • D: 시간 순서에 따른 객체 간 메시지 전달을 표현하는 것은 시퀀스 다이어그램이다. 액티비티 다이어그램은 처리 흐름과 로직 표현에 쓰인다.

🔍 피드백: B를 정확히 골랐습니다. D는 시퀀스 다이어그램에 대한 설명이라는 근거가 정확합니다. 다만 A는 시퀀스 다이어그램의 도형을 반대로 바꾼 보기이고(객체는 네모 상자, 메시지는 화살표), C는 시퀀스 다이어그램이 메시지 처리 방식을 표현하는 데 한계가 있다는 점을 반대로 쓴 보기입니다. 두 보기 모두 “액티비티 다이어그램에 대한 설명”은 아니니 이 부분은 정리해 두세요.

출처: server/game_server/5/2026-02-12-game_server_5_1.md


Q3. 🟡 보통 — 🔺 7.5/10

문제: 빈칸을 채우시오.

C++이 클래스에 대해 암시적으로 생성하는 멤버 함수에 대한 설명이다.

  • 개발자가 선언하지 않으면 컴파일러가 대신 만들어 주는 멤버 함수는 기본 생성자, 소멸자, 복사 생성자, 그리고 ① ___이다. (함수 이름)
  • 클래스에 생성자가 하나라도 선언되어 있으면, 컴파일러는 ② ___를 생성하지 않는다. (함수 이름)
  • 컴파일러가 생성한 소멸자는, 이 클래스가 상속한 기본 클래스의 소멸자가 가상(virtual)이 아니면 ③ ___ 소멸자가 된다. (두 글자 수식어)
  • 컴파일러가 생성한 복사 생성자는 int 같은 기본 제공 타입 멤버에 대해서는 각 ④ ___을(를) 그대로 복사한다. (한 단어)

📝 내 답: 복사 대입 연산자, 기본 생성자, 비가상, 자원

정답:

① 복사 대입 연산자 ② 기본 생성자 ③ 비가상 ④ 비트

해설:

컴파일러가 대신 생성해 주는 것은 기본 생성자, 소멸자, 복사 생성자, 복사 대입 연산자 4가지다. 생성자가 이미 선언되어 있으면 기본 생성자는 생성하지 않는다. 생성된 소멸자는 기본 클래스의 소멸자가 가상이 아니면 비가상 소멸자로 만들어진다. 복사 생성자는 string 같은 타입은 그 타입의 복사 생성자로 초기화하고, int 같은 기본 제공 타입은 각 비트를 그대로 복사한다.

🔍 피드백: ①②③은 맞았습니다(각 2.5점). ④는 “자원”이라고 쓰셨는데, 기본 제공 타입 멤버는 값을 담은 비트를 그대로 복사합니다. string 같은 클래스 타입 멤버는 그 타입 자체의 복사 생성자로 초기화되는 것과 대비해서 기억해 두세요.

출처: game_dev/cpp/chapter2/2025-04-22-cpp_2_5.md


Q4. 🟡 보통 — ⭕ 10/10

문제: 다음 코드는 std::async를 기본 시동 방침으로 호출한 뒤 wait_for로 완료를 기다린다. 이에 대한 설명으로 가장 올바른 것은?

void f() { std::this_thread::sleep_for(1s); }

auto fut = std::async(f);              // 시동 방침을 명시하지 않음

while (fut.wait_for(100ms) != std::future_status::ready) {
	…
}
  • A. 기본 시동 방침은 std::launch::async와 같으므로, f는 항상 다른 스레드에서 실행된다.
  • B. 기본 시동 방침에서 f가 지연(deferred)되면 wait_for가 항상 std::future_status::deferred를 돌려주므로, 이 루프는 종료되지 않을 수 있다.
  • C. std::launch::deferred로 시동된 f는 get이나 wait를 한 번도 호출하지 않아도 결국 실행된다.
  • D. f가 thread_local 변수를 사용하더라도, 어느 스레드에서 실행되든 접근하는 변수는 항상 같으므로 신경 쓸 필요가 없다.

📝 내 선택: B

정답: B

해설:

f가 지연되면 fut.wait_for는 항상 std::future_status::deferred를 돌려주는데, 이 값은 ready와 절대 같지 않으므로 루프가 종료되지 않는다. 이런 버그는 부하가 적을 땐 드러나지 않아 개발 중에 놓치기 쉽다. 해결책은 만료 시간을 0으로 wait_for(0s)를 호출해 지연 여부를 먼저 점검하거나, std::launch::async를 지정하는 것이다.

  • A: 기본 시동 방침은 async가 아니라 std::launch::async | std::launch::deferred(OR 결합)이다. 비동기일 수도, 동기(지연)일 수도 있다.
  • C: deferred로 시동된 f는 get/wait가 호출될 때에만 실행되고, 호출되지 않으면 결코 실행되지 않는다.
  • D: 기본 시동 방침에서는 어느 스레드의 thread_local 변수에 접근할지 예측할 수 없다.

🔍 피드백: B를 정확히 골랐고, A(기본 시동 방침은 항상 다른 스레드 실행이 아님), C(get/wait 호출이 있어야 실행) 근거도 맞습니다. D는 “해당 스레드에서만 사용된다”고 하셨는데, 정확히는 기본 시동 방침에서는 f가 어느 스레드에서 실행될지(비동기 스레드인지, get/wait를 호출한 스레드인지) 예측할 수 없으므로 어느 스레드의 thread_local 변수에 접근하는지도 예측할 수 없다는 점이 핵심입니다.

출처: game_dev/moderncpp/7/2026-10-07-moderncpp_7_36.md


Q5. 🟢 쉬움 — ⭕ 10/10

문제: 다음 명제가 참(O)인지 거짓(X)인지 판단하라.

C++11에서도 비const 컨테이너 values에서 const_iterator를 얻으려면 C++98처럼 static_cast<const_iterator>(values.begin())처럼 캐스팅하는 방법밖에 없다. cbegin()과 cend()는 비const 컨테이너에 대해서는 iterator를 돌려주기 때문이다.

📝 내 답: X. C++11부터는 vlaues.cbegin()을 사용할 수 있으며, const_iterator를 돌려준다.

정답: X

해설:

C++11의 컨테이너 멤버 함수 cbegin()과 cend()는 비const 컨테이너에서도 const_iterator를 돌려준다. 따라서 std::find(values.cbegin(), values.cend(), 1983)처럼 캐스팅 없이 const_iterator를 얻을 수 있다. insert 같은 STL 멤버 함수들도 C++11부터 실제로 const_iterator를 받는다.

🔍 피드백: 판단(X)과 근거(cbegin()이 const_iterator를 돌려줌)가 모두 정확합니다.

출처: game_dev/moderncpp/3/2026-05-02-moderncpp_3_13.md


Q6. 🟡 보통 · 📌 오답노트 — ⭕ 10/10

문제: 다음 명제가 참(O)인지 거짓(X)인지 판단하라.

하나의 스위치가 연결할 수 있는 단말기 수에는 한계가 있고, 단말기가 많아질수록 고유한 MAC 주소를 관리하기도 어려워진다. 이 문제를 해결하기 위해 3계층의 라우터는 MAC 주소를 기준으로 여러 LAN 사이에서 프레임을 전달한다.

📝 내 답: X. 첫 문장까지는 맞다. 두번째 문장에서 잘못되었는데, 3계층의 라우터는 MAC 주소를 기준으로 하는게 아니라, IP 주소를 기준으로 전달한다.

정답: X

해설:

앞 문장은 맞다. 2계층(데이터 링크 계층)에서는 하나의 스위치가 연결할 수 있는 단말기 수에 한계가 있고, 단말기가 많아지면 MAC 주소 관리가 어려워진다. 이를 해결하는 것이 WAN이다. 그러나 3계층(네트워크 계층)의 라우터는 IP 주소를 기준으로 여러 네트워크(LAN) 사이에서 경로를 설정하고 데이터를 전달한다. MAC 주소와 프레임 단위로 통신하는 것은 2계층(스위치)의 일이다.

🔍 피드백: 판단(X)과 틀린 위치(두 번째 문장), 올바른 내용(라우터는 IP 기준)을 모두 정확히 짚었습니다.

출처: server/game_server/2/2025-06-30-game_server_2_1.md 오답노트: wn-34e57a32 · 2026-09-09 최초 오답 · 이번이 3번째 복습


📌 복습 포인트

  • Q1 템플릿 메타프로그래밍 — TMP의 정의(“컴파일 도중 실행되는 템플릿 프로그램”)와 if 분기가 컴파일 시점에 모든 분기를 생성하는 문제 → game_dev/cpp/chapter7/2025-11-03-cpp_7_48.md
  • Q3 암시적 생성 멤버 함수 — 기본 제공 타입 멤버는 복사 생성자가 비트 단위로 복사 → game_dev/cpp/chapter2/2025-04-22-cpp_2_5.md

MallangQuiz 카테고리 내 다른 글 보러가기

댓글남기기