[말랑 퀴즈] 26/11/13 해답

게시:     수정

카테고리:

태그:

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

말랑말랑 퀴즈 — 해답지 ✅

날짜: 2026-11-13 문제 수: 6문제 총점: 46.8/60 (78점) ✅ 잘했어요

문제 생성일은 2026-11-03이며, 이 해답의 날짜는 실제 풀이일인 블로그 포스트 날짜를 따릅니다.

📊 채점 결과

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

총평

IOCP와 epoll의 차이, 비멤버 연산자와 friend의 관계, 보편 참조에 std::move를 적용했을 때의 문제를 잘 이해했습니다. Q1은 근거가 맞지만 O/X 선택이 반대였고, Q3은 출처에서 사용하는 대기 상태 용어를 구분할 필요가 있습니다. Q6은 복사·소멸 횟수와 가상 함수 호출 대상을 정확히 짚었으나, 복사를 막는 참조와 수정을 제한하는 const의 역할, 슬라이싱, 값 전달에 적합한 타입 세 가지를 보완하면 좋겠습니다.


Q1. 🟢 쉬움 — 🔺 3/10

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

복사를 막고 싶은 클래스에서 복사 생성자와 복사 대입 연산자를 private로 선언만 하고 구현하지 않으면, 객체를 복사하려는 시도는 링크 시점에 에러가 난다. Uncopyable처럼 복사를 막아 둔 클래스를 상속하면, 이 에러가 링크 시점보다 더 늦은 실행 시점의 에러로 옮겨진다.

📝 내 답:

O. Uncopyable을 상속하게 되면 에러가 복사를 시도했을 시 컴파일 시점에 발생한다.

정답: X

해설:

Uncopyable을 상속하면 복사 시도의 오류를 실행 시점이 아니라 더 이른 컴파일 시점에 발견할 수 있다. 따라서 명제는 거짓이다.

원본 해답의 앞 문장에는 조건 보충이 필요하다. 복사 함수를 private로 선언만 했다면, 일반 외부 코드의 복사 시도는 접근 제한 때문에 이미 컴파일 오류다. 그 클래스의 멤버나 friend처럼 private 접근이 가능한 코드에서 정의 없는 복사 함수를 호출하면 링크 오류가 난다. Uncopyable의 private 복사 함수에는 파생 클래스의 멤버나 friend도 접근할 수 없으므로, 이 경로의 오류도 컴파일 시점에 잡을 수 있다.

🔍 피드백: O/X 판단은 O를 명시했으므로 0/7점입니다. 다만 적어 준 “컴파일 시점에 발생한다”는 근거는 정확하므로 3/3점을 인정합니다. 그 근거는 명제의 “실행 시점”을 부정하므로 선택은 X여야 합니다.

출처: game_dev/cpp/chapter2/2025-04-23-cpp_2_6.md


Q2. 🟡 보통 — ⭕ 10/10

문제: 윈도우의 IOCP와 리눅스의 epoll을 비교한 설명으로 가장 올바른 것은?

  • A. IOCP는 소켓 하나에 대한 완료 신호를 스레드 하나만 처리하게 보장할 수 있어서, IOCP 하나를 여러 스레드가 기다리게 구현하기 쉽다.
  • B. epoll은 Overlapped I/O를 이용해 블로킹을 없앤다.
  • C. IOCP는 리눅스와 안드로이드에서, epoll은 윈도우에서 사용하는 기술이다.
  • D. IOCP는 I/O 입력이 가능해졌을 때 알려 주는 준비 완료 기반이고, epoll은 I/O가 완료되었을 때 알려 주는 완료 기반이다.

📝 내 답:

A

B: overlapped I/O는 윈도우에서 사용하는 방식이며, epoll에서는 논블록 소켓을 이용한다. C: 반대다. D: 반대다.

정답: A

해설:

IOCP는 요청해 둔 Overlapped I/O의 완료를 통지하므로, 완료를 처리한 뒤 다음 요청을 거는 방식으로 같은 소켓의 처리 흐름을 제어할 수 있다. 하나의 IOCP를 여러 작업 스레드가 기다리는 스레드 풀을 구성하기에도 적합하다.

다만 IOCP가 소켓별 처리를 무조건 자동 직렬화하는 것은 아니다. 같은 소켓에 여러 I/O를 동시에 요청하면 여러 완료가 병렬 처리될 수 있으므로, A의 “보장할 수 있다”는 요청·동시성 설계를 전제로 이해해야 한다.

  • B: Overlapped I/O는 Windows 방식이다. epoll을 이용한 비차단 처리는 일반적으로 논블록 소켓과 함께 구현한다.
  • C: IOCP는 Windows, epoll은 Linux 및 Android에서 사용한다.
  • D: IOCP는 완료 기반, epoll은 준비 완료 기반이다.

🔍 피드백: 정답 A를 골랐고, B·C·D가 틀린 이유도 정확합니다. 특히 준비 완료와 작업 완료의 차이를 잘 구분했습니다.

출처: server/game_server/3/2026-02-07-game_server_3_9.md


Q3. 🟢 쉬움 — 🔺 7.5/10

문제: 빈칸을 채우시오.

블로킹 소켓에 대한 설명이다.

  • 블로킹이란 디바이스에 처리 요청을 걸고 응답을 대기하는 함수를 호출할 때 스레드에서 발생하는 ① ___ 현상이다. (두 글자)
  • 블로킹된 스레드는 ② ___ state 상태가 된다. (영문 소문자 한 단어)
  • 이때 그 스레드의 CPU 사용량은 ③ ___%가 된다. (숫자)
  • 스레드에서 네트워크 수신 함수를 호출했는데 상대 컴퓨터가 데이터를 보내지 않으면 ④ ___ 블로킹이 발생한다. (세 글자 부사)

📝 내 답:

대기, idle, 0, 무한정

정답:

① 대기
② waitable
③ 0
④ 영원히 — “무한정”도 인정

해설:

블로킹은 요청에 대한 응답을 기다리는 대기 현상이다. 원본 해답과 출처는 이때의 상태를 waitable state라고 표현한다. 블로킹되어 기다리는 동안 해당 스레드는 CPU를 사용하며 계속 확인하는 것이 아니므로, 문제의 모델에서 CPU 사용량은 0%다. 데이터 도착이나 종료·오류·타임아웃 등 대기를 끝내는 사건이 없다면 수신 대기는 무한정 이어질 수 있다.

용어는 구분해서 알아 두자. 일반적인 스레드 상태 설명에는 waiting/blocked가 쓰이며, 원본의 waitable을 모든 운영체제의 표준 스레드 상태명으로 외우지는 않아도 된다. idle은 CPU의 유휴 상태나 유휴 스레드 등을 가리키는 용어여서, 이 빈칸에서 요구하는 I/O 대기 상태를 정확히 특정하지 못한다.

🔍 피드백: 빈칸당 2.5점입니다. ①·③은 정확하고, ④ “무한정”은 “영원히”와 같은 뜻이므로 인정합니다. ② idle은 원본의 waitable 및 이에 해당하는 waiting/blocked 상태와 구분해야 하므로 0점입니다. 합계 7.5점입니다.

출처: server/game_server/3/2025-12-16-game_server_3_1.md


Q4. 🟡 보통 — ⭕ 10/10

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

Rational의 생성자 Rational(int numerator = 0, int denominator = 1)이 explicit가 아니라서 int가 Rational로 암시적 변환된다고 하자. operator*를 멤버 함수로 선언하면 oneHalf * 2는 컴파일되지만 2 * oneHalf는 컴파일되지 않는다. 2 * oneHalf도 되게 하려고 operator*를 비멤버 함수로 만들 때, 이 함수는 Rational의 public 인터페이스만으로 구현할 수 있더라도 friend로 선언하는 것이 바람직하다.

📝 내 답:

X. friend를 피할수 있다면 피하는 것이 좋다.

정답: X

해설:

앞부분은 맞다. 멤버 함수 oneHalf.operator*(2)에서는 인수 2를 임시 Rational로 암시적 변환할 수 있다. 반대로 정수 2에는 해당 멤버가 없고, 이를 해결할 비멤버 연산자도 없으면 2 * oneHalf는 컴파일되지 않는다.

두 인수 모두에 암시적 변환을 허용하려면 operator*를 비멤버 함수로 만들면 된다. 그 구현이 numerator()와 denominator() 등 public 인터페이스만 사용한다면 private 접근 권한을 주는 friend 선언은 불필요하다. 멤버 함수의 반대는 프렌드 함수가 아니라 비멤버 함수다.

🔍 피드백: X 판단 7점, 불필요한 friend를 피해야 한다는 근거 3점으로 만점입니다. 문제의 “public 인터페이스만으로 구현 가능”이라는 조건에서 friend가 필요 없다는 핵심을 짚었습니다.

출처: game_dev/cpp/chapter4/2025-06-25-cpp_4_24.md


Q5. 🟡 보통 — ⭕ 10/10

문제: 아래 코드에서 w.setName(n);을 실행한 직후의 상황으로 가장 올바른 것은?

class Widget {
public:
	template<typename T>
	void setName(T&& newName)          // 보편 참조
	{ name = std::move(newName); }
private:
	std::string name;
};

std::string getWidgetName();            // 팩터리 함수
Widget w;
auto n = getWidgetName();               // n은 지역 변수 (lvalue)

w.setName(n);
  • A. n의 내용이 w.name으로 이동해 버려서, n의 값은 알 수 없는 상태가 된다.
  • B. n은 lvalue이므로 std::move가 무시되어, n의 값이 그대로 유지된 채 w.name으로 복사된다.
  • C. std::move를 보편 참조에 적용했으므로 컴파일 오류가 난다.
  • D. n의 값은 그대로 유지되고, w.name은 비어 있는 상태가 된다.

📝 내 답:

A

B: n은 lvalue이지만 move를 적용할때 rvalue로 캐스팅되어, 값이 이전될수 있다. C: 컴파일 오류가 나지는 않는다. D: 둘다 비어있게 될수 있다.

정답: A

해설:

n을 넘기면 T는 std::string&로 추론되고, 참조 축약에 따라 newName도 n을 참조한다. 그러나 함수 안의 std::move(newName)이 이를 rvalue로 캐스팅하여 name의 이동 대입을 유도한다. 따라서 호출자가 lvalue로 넘긴 n도 이동 대상이 된다.

이동 후 n은 유효하지만 값은 미지정인 상태다. 비어 있다고 단정할 수 없으며, 잘못된 객체가 되는 것도 아니다. 보편 참조에는 std::forward<T>(newName)을 사용해야 lvalue 인수는 복사하고 rvalue 인수는 이동하도록 전달할 수 있다.

  • B: std::move는 lvalue에도 적용된다.
  • C: 이 코드는 컴파일된다.
  • D: 이동 대입의 대상인 w.name에는 이동 전 문자열 값이 전달된다. 이동 전 n이 비어 있지 않았다면 둘 다 빈 문자열이 되는 설명은 맞지 않는다. 처음부터 빈 문자열이었다면 둘 다 빌 수는 있지만, 그것이 D가 틀린 핵심 이유는 아니다.

🔍 피드백: 객관식 정답 A를 골랐으므로 10점입니다. B·C의 설명도 정확합니다. D에 덧붙인 “둘 다 비어 있을 수 있다”보다는 받는 쪽은 이동 전 값을 받고, 원본의 이동 후 값은 보장하지 않는다고 구분해 두면 더 정확합니다.

출처: game_dev/moderncpp/5/2026-09-16-moderncpp_5_25.md


Q6. 🔴 어려움 · 📌 오답노트 — 🔺 6.3/10

문제: C++에서 함수에 객체를 전달하는 방식에 대해 답하시오. 아래 클래스를 사용한다.

class Monster {
public:
	Monster();
	virtual ~Monster();
	virtual void attack() const;
private:
	string name;
	string type;
};

class Boss : public Monster {
public:
	Boss();
	~Boss();
	virtual void attack() const;
private:
	string skill;
	string loot;
};

bool check_Val(Boss b);
bool check_Ref(const Boss& b);

void act_Val(Monster m) { m.attack(); }
void act_Ref(const Monster& m) { m.attack(); }

(1) Boss boss; 다음에 check_Val(boss);를 호출하면 복사 생성자가 총 몇 번 호출되는가? 어떤 객체들의 복사 생성자인지 모두 쓰시오. 함수가 끝날 때의 소멸자 호출 횟수는 몇 번인가?

(2) check_Ref(boss);를 호출하면 복사 생성자는 호출되는가? 이때 const는 어떤 역할을 하는가?

(3) act_Val(boss);와 act_Ref(boss);를 호출했을 때, 각각 어느 클래스의 attack()이 실행되는가? 그 이유를 쓰고, act_Val에서 일어나는 현상의 이름을 쓰시오.

(4) 위 설명에도 불구하고 값 전달이 오히려 나은 타입 세 가지를 쓰시오.

📝 내 답:

(1) 6번이며, Boss, skill, loot, Monster, name, type. 소멸자 호출 횟수는 6번

(2) const로 인해 호출되지 않는다. 상수 객체임을 보장한다.

(3) act_Val은 Monster의 attack, act_Ref는 Boss의 attack. 참조나 포인터로 Boss 객체를 가지고 있으면, 가상 함수 포인터와 가상 함수 테이블로 인해, 오버라이드된다. 가상 함수 오버라이드

(4) int같은 기본 자료형인 경우, 해당 함수 내부에서만 사용되는 경우, 원본의 값이 변경되면 안되는 경우

정답:

(1) 복사 생성자는 총 6번 호출된다. Boss 1번, 기본 클래스 부분인 Monster 1번, Monster의 string 멤버 name·type 2번, Boss의 string 멤버 skill·loot 2번이다. 값 매개변수가 소멸할 때의 소멸자 호출도 총 6번이다. 최초의 Boss boss;에서 일어나는 기본 생성이나 나중에 원본 boss가 소멸하는 것은 이 횟수에 포함하지 않는다.

(2) 복사 생성자는 호출되지 않는다. 복사를 피하는 이유는 참조(&)로 전달하기 때문이다. const는 이 참조를 통해 객체를 수정하지 못하도록 제한한다. 원본 객체 자체를 새로 상수 객체로 바꾸는 것은 아니다. Boss&도 복사 없이 전달되지만 수정을 허용하고, const Boss를 값으로 받으면 const가 있어도 복사는 일어날 수 있다.

(3) act_Val(boss)는 Monster::attack(), act_Ref(boss)는 Boss::attack()을 실행한다. 값 전달에서는 Monster m = boss;처럼 기본 클래스 부분만 복사하여 별도의 Monster 객체를 만든다. 파생 클래스 부분이 잘리는 이 현상을 객체 슬라이싱(object slicing, 복사손실)이라고 한다. 가상 함수가 있어도 이미 Monster 객체가 된 매개변수에서 Boss의 기능이 복구되지는 않는다. 참조 전달은 원본 Boss 객체를 그대로 가리키므로 가상 호출이 Boss::attack()으로 이어진다. 가상 함수 테이블은 이를 구현하는 일반적인 방식이며, 슬라이싱 자체의 이름은 아니다.

(4) 원본 해답에서 요구하는 세 가지는 기본제공 타입, STL 반복자, 함수 객체 타입이다. 예를 들어 int·double, 가벼운 반복자, 가벼운 함수 객체다. 다만 큰 상태를 가진 함수 객체 등은 복사 비용을 따져야 하므로 모든 반복자·함수 객체가 무조건 값 전달에 유리하다는 뜻은 아니다.

🔍 피드백: 네 소문항에 각각 2.5점을 배정했습니다.

  • (1) 2.5/2.5점: 복사 대상 여섯 개와 소멸자 횟수가 모두 정확합니다. 목록은 호출 순서를 묻는 것이 아니므로 나열 순서로 감점하지 않았습니다.
  • (2) 1.5/2.5점: 복사가 없다는 결론과 상수 접근이라는 취지는 인정합니다. 다만 원인을 const로 적은 것은 틀렸습니다. 복사 방지는 참조, 그 참조를 통한 수정 제한은 const로 구분해야 합니다.
  • (3) 1.5/2.5점: 두 호출 대상과 참조·포인터에서 가상 호출이 작동한다는 설명은 맞습니다. 하지만 값 전달로 기본 클래스 부분만 복사된다는 이유가 빠졌고, 요구한 현상 이름은 “가상 함수 오버라이드”가 아닌 슬라이싱입니다.
  • (4) 0.8/2.5점: 기본제공 타입 한 가지를 맞혔으므로 세 타입 중 하나에 해당하는 점수를 소수 첫째 자리로 반올림해 인정합니다. “함수 내부에서만 사용”, “원본을 변경하면 안 됨”은 타입이 아니라 사용 조건입니다. 특히 원본 수정 방지는 const T&로도 가능하므로 값 전달이 더 낫다는 근거가 되지 않습니다. 빠진 두 가지는 STL 반복자와 함수 객체 타입입니다.

합계 6.3/10점입니다.

출처: game_dev/cpp/chapter4/2025-06-18-cpp_4_20.md 오답노트: wn-7f70f538 · 2026-09-18 최초 오답 · 이번이 2번째 복습


📌 복습 포인트

  • Q1 복사 금지와 오류 시점 — 근거가 “컴파일 시점”이면 “실행 시점으로 늦어진다”는 명제는 X다. private 접근 위반과 정의 누락에 따른 링크 오류도 구분하자. → game_dev/cpp/chapter2/2025-04-23-cpp_2_6.md
  • Q3 블로킹 상태 용어 — I/O를 기다리는 waiting/blocked 상태를 idle과 구분하자. 이 문제의 원본 표기는 waitable이다. “무한정”과 “영원히”는 같은 뜻으로 인정된다. → server/game_server/3/2025-12-16-game_server_3_1.md
  • Q6 참조·const·슬라이싱 — 복사는 참조가 피하고 const는 수정 접근을 제한한다. 기본 클래스 값 매개변수에서는 슬라이싱이 생긴다. 값 전달에 적합한 타입으로 기본제공 타입·STL 반복자·함수 객체를 정리하자. → game_dev/cpp/chapter4/2025-06-18-cpp_4_20.md

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

댓글남기기