[말랑 퀴즈] 26/10/19 해답
카테고리: MallangQuiz
태그: Quiz
제 말랑말랑 퀴즈 생성기는 이곳에서 확인하실 수 있습니다.
말랑말랑 퀴즈 — 해답지 ✅
날짜: 2026-10-19 문제 수: 6문제 총점: 50/60 (83점) ✅ 잘했어요
📊 채점 결과
| 문항 | 유형 | 난이도 | 결과 | 점수 |
|---|---|---|---|---|
| Q1 | OX | 🟢 쉬움 | ⭕ 정답 | 10 / 10 |
| Q2 | 객관식 | 🟡 보통 | ⭕ 정답 | 10 / 10 |
| Q3 | 빈칸 채우기 | 🟡 보통 | ⭕ 정답 | 10 / 10 |
| Q4 | OX | 🟡 보통 | ⭕ 정답 | 10 / 10 |
| Q5 | 객관식 | 🟡 보통 | ❌ 오답 | 0 / 10 |
| Q6 📌 | 서술형 | 🔴 어려움 | ⭕ 정답 | 10 / 10 |
| 합계 | 50 / 60 |
총평
다섯 문제를 만점으로 풀었고, 특히 오답노트 복습 문제 Q6(const)은 포인터·반복자·상수성·mutable까지 네 소문항을 모두 정확히 답했습니다. 유일하게 놓친 Q5는 std::move의 인수가 const std::string이라는 점을 놓친 문제입니다. const 객체에 std::move를 써도 이동 생성자가 호출되지 않습니다.
Q1. 🟢 쉬움 — ⭕ 10/10
문제: 다음 명제가 참(O)인지 거짓(X)인지 판단하라.
플레이어 간 상호 작용(PVP)은 정확해야 하고, 지연 시간에 큰 영향을 받으며, 기관총 발사처럼 횟수가 매우 잦을 때도 있다. 그래서 플레이어 간 상호 작용은 일반적으로 여러 서버에 분산하여 처리하는 것이 권장된다.
📝 내 답: X. 잦으며 응집도가 큰 작업은 하나의 서버에서 하는 것이 권장된다
정답: X
해설:
앞 문장의 특징(정확성, 높은 성능, 잦은 횟수, 원자성, 지연 시간의 영향)은 모두 맞다. 그러나 이런 특징 때문에 플레이어 간 상호 작용은 분산하지 않는 것이 일반적이다. 분산해야 한다면 응집도가 낮은 플레이어들(예: 지역적 분산 처리)을 분산한다.
🔍 피드백: 판단(X)과 근거(잦고 응집도가 큰 작업은 한 서버에서 처리)가 모두 정확합니다.
출처:
server/game_server/9/2026-03-29-game_server_10_5.md
Q2. 🟡 보통 — ⭕ 10/10
문제: 아래 그림은 IP 패킷의 구조다. IP 패킷, 스트림, 메시지에 대한 설명으로 가장 올바른 것은?
- A. IP 패킷 하나의 크기는 제한되어 있지만, 네트워크 스택이 데이터를 여러 조각으로 나누어 보내고 수신자가 조립해 복원해 주므로, 스트림과 메시지를 쓰는 쪽에서는 패킷 크기 제한을 신경 쓸 필요가 없다.
- B. 스트림은 송신자가 보낸 데이터 개수와 수신자가 받는 데이터 개수가 같고, 보낸 데이터의 시작과 끝이 받는 데이터와 같다.
- C. 메시지는 자체적으로 데이터의 시작과 끝을 구별하지 못하므로, 사용자가 직접 데이터를 끊어서 읽어야 한다.
- D. IP 패킷의 체크섬은 수신자가 송신자를 식별하는 용도로 쓰인다.
📝 내 선택: A
정답: A
해설:
IP 패킷 하나의 크기는 제한되어 있지만, 운영체제의 네트워크 스택이 단편화(IP 패킷 크기 제한에 맞춰 여러 조각으로 나누는 것)를 수행하고 수신자가 조각들을 조립해 복원하므로, 스트림과 메시지는 패킷 크기 제한을 신경 쓸 필요가 없다.
- B: 보낸 개수와 받는 개수가 같고 시작과 끝이 같은 것은 메시지의 특징이다. 스트림은 보낸 데이터와 받은 데이터가 완전히 같지 않을 수 있다.
- C: 데이터의 시작과 끝을 자체적으로 구별할 수 있는 것이 메시지이고, 끊는 것이 사용자의 몫인 쪽이 스트림이다.
- D: 수신자가 송신자를 식별하는 용도는 송신자 주소다. 체크섬은 데이터 전송 중의 오류를 감지하는 데 쓰인다.
🔍 피드백: A를 정확히 골랐고, B(메시지의 특징), C(스트림의 특징), D(체크섬은 무결성/오류 감지용) 근거가 모두 맞습니다.
출처:
server/game_server/2/2025-07-10-game_server_2_3.md
Q3. 🟡 보통 — ⭕ 10/10
문제: 빈칸을 채우시오.
MongoDB에서 컬렉션 coll1의 도큐먼트를 다루는 명령에 대한 설명이다.
- 컬렉션에 도큐먼트
{"a":1}을 추가하는 명령은db.coll1.①___({"a":1})이다. (SQL의 insert에 대응하는 함수 이름, 영문 소문자) - 조건에 맞는 도큐먼트 중 첫 번째로 나오는 하나만 출력하는 함수는
db.coll1.②___()이다. (영문 camelCase 함수 이름) a가 1인 도큐먼트의 값을 5로 바꾸는 명령은db.coll1.update({"a":1}, {$③___:{"a":5}})이다. (달러 기호 뒤에 오는 영문 소문자 세 글자, 값을 지정한다는 뜻)a가 3인 도큐먼트를 삭제하는 명령은db.coll1.④___({"a":3})이다. (SQL의 delete에 대응하는 함수 이름, 영문 소문자)
📝 내 답: insert, findOne, set, remove
정답:
① insert ② findOne ③ set ④ remove
해설:
MongoDB의 CRUD는 insert(생성), find/findOne(읽기, findOne은 첫 번째로 나오는 하나만 출력), update와 $set(업데이트), remove(삭제)로 한다. SQL의 insert, select, update ... set, delete와 각각 비슷하다.
🔍 피드백: 네 칸 모두 정확합니다.
출처:
server/game_server/8/2026-03-11-game_server_8_6.md
Q4. 🟡 보통 — ⭕ 10/10
문제: 다음 명제가 참(O)인지 거짓(X)인지 판단하라.
클래스 템플릿
Widget<T>에 대해swap을 지원하려고std네임스페이스 안에template<typename T> void swap(Widget<T>& a, Widget<T>& b)를 오버로딩으로 추가하면, 컴파일도 되고 표준에도 어긋나지 않으므로 안전한 해결 방법이다.
📝 내 답: X. std 네임스페이스에 완전 템플릿 특수화는 허용하지만, 템플릿 추가는 불가능하다.
정답: X
해설:
이 코드는 컴파일은 되지만, std 네임스페이스에 새로운 템플릿 함수를 추가하는 것은 표준 위반이라서 실행 결과가 미정의 동작(UB)이다. std 안의 기존 템플릿에 대한 완전 특수화는 가능하지만 새 템플릿을 추가할 수는 없다. 올바른 방법은 Widget이 들어 있는 사용자 네임스페이스 안에 비멤버 swap을 오버로딩으로 제공하는 것이다(인자 기반 탐색(ADL)으로 선택됨).
🔍 피드백: 판단(X)과 근거(std에는 완전 특수화만 허용되고 새 템플릿 추가는 불가)가 정확합니다. 이 주제는 이전에 약했는데 이번에는 정확히 정리하셨습니다. 한 가지 덧붙이면, 표준 위반의 결과가 미정의 동작이고 올바른 해법은 사용자 네임스페이스의 비멤버 swap(+ ADL)입니다.
출처:
game_dev/cpp/chapter4/2025-07-02-cpp_4_25.md
Q5. 🟡 보통 — ❌ 0/10
문제: 아래 코드에서 value(std::move(text))를 실행할 때 실제로 호출되는 std::string의 생성자는 무엇인가?
class Annotation {
public:
explicit Annotation(const std::string text)
: value(std::move(text))
{ … }
private:
std::string value;
};
- A. 이동 생성자
- B. 복사 생성자
- C. 어느 생성자도 호출되지 않고 컴파일 오류가 난다.
- D. 기본 생성자
📝 내 선택: A. move 함수는 통해 string의 이동 생성자를 호출한다.
정답: B
해설:
text가 const std::string이므로 std::move(text)의 결과는 const std::string&&, 즉 const rvalue다. std::string의 이동 생성자는 const가 아닌 std::string&&를 받아 이 인수를 받을 수 없다. 반면 복사 생성자는 const std::string&를 받아 const rvalue도 바인딩할 수 있으므로 결국 복사 생성자가 호출된다(컴파일 오류도 아니다). 이동을 지원할 객체는 const로 선언하지 말아야 하며, std::move는 실제로 아무것도 이동하지 않고 결과가 rvalue라는 것만 보장한다.
🔍 피드백: std::move가 이동 생성자를 호출한다고 생각하신 것이 함정에 걸린 부분입니다. 이 문제의 핵심은 매개변수가 const std::string이라는 점입니다. std::move는 아무것도 이동시키지 않고 rvalue로 캐스팅할 뿐이며, 결과 타입이 const std::string&&이라서 std::string&&를 받는 이동 생성자는 쓸 수 없고, const std::string&를 받는 복사 생성자가 호출됩니다. 이동을 지원하려면 const를 붙이지 마세요.
출처:
game_dev/moderncpp/5/2026-09-10-moderncpp_5_23.md
Q6. 🔴 어려움 · 📌 오답노트 — ⭕ 10/10
문제: C++의 const에 대해 답하시오.
(1) 아래 세 포인터 선언에서 각각 포인터 자체를 수정할 수 있는지, 가리키는 데이터를 수정할 수 있는지 쓰시오.
char greeting[] = "Hello";
const char * p1 = greeting;
char * const p2 = greeting;
const char * const p3 = greeting;
(2) 아래 코드에서 const vector<int>::iterator it와 vector<int>::const_iterator cit는 각각 *it = 10;, ++it;, *cit = 10;, ++cit; 중 무엇이 가능하고 무엇이 불가능한가?
vector<int> vec;
const vector<int>::iterator it = vec.begin();
vector<int>::const_iterator cit = vec.begin();
(3) 아래 코드는 operator[]가 const 멤버 함수이므로 컴파일은 되지만, 상수 객체 cctb의 내용이 바뀐다. 이런 일이 벌어지는 이유를 “비트수준(물리적) 상수성”의 정의와 연결해 설명하고, 이를 보완하기 위해 나온 개념(논리적 상수성)의 정의를 쓰시오.
class CTextBlock {
public:
char& operator[](size_t position) const { return pText[position]; }
private:
char* pText;
};
const CTextBlock cctb("Hello");
char* pc = &cctb[0];
*pc = 'J';
(4) 아래 length()는 값을 캐시해 두려고 const 멤버 함수 안에서 멤버를 수정하다가 에러가 난다. 에러가 나는 이유와 해결 방법(키워드와 그것을 붙일 대상)을 쓰시오.
class CTextBlock {
public:
size_t length() const;
private:
char* pText;
size_t textLength;
bool lengthIsValid;
};
size_t CTextBlock::length() const {
if(!lengthIsValid) {
textLength = strlen(pText);
lengthIsValid = true;
}
return textLength;
}
📝 내 풀이:
(1) const char * 는 포인터 O, 데이터 X char * const 는 포인터 X, 데이터 O const char * const는 포인터 X, 데이터 X
(2) *it=10 O, ++it X, *cit=10 X, ++cit O
(3) 비트수준 상수성은 어떤 비정적 데이터 멤버라도 수정이 되지 않아야 const라는 규칙이다. 하지만 내부에서 지켜진다 하더라도, 내부 참조값을 반환해버리는 경우 이러한 규칙이 깨진다. 이를 위해 나온것이 논리적 상수성으로, 외부에서 알아차리지 못하게 내부 상태를 수정하면 괜찮다는 규칙이다.
(4) textLength와 lengthIsValid의 값을 변경하고 있기 때문이며, 이들 자료형 앞에 mutable을 붙여주면 된다.
정답:
(1) p1(const char *): 포인터 수정 O, 데이터 수정 X. p2(char * const): 포인터 수정 X, 데이터 수정 O. p3(const char * const): 포인터 수정 X, 데이터 수정 X. (const가 *의 왼쪽에 있으면 데이터가 상수, 오른쪽에 있으면 포인터가 상수다.)
(2) const vector<int>::iterator it는 T * const처럼 동작한다: *it = 10; 가능(데이터 수정 O), ++it; 불가능(포인터/주소 수정 X). vector<int>::const_iterator cit는 const T *처럼 동작한다: *cit = 10; 불가능(데이터 수정 X), ++cit; 가능(주소 수정 O).
(3) 비트수준 상수성은 멤버 함수가 객체의 어떤 데이터 멤버도 건드리지 않아야 const로 인정하는 것이다. 이 함수는 pText 멤버 자체를 수정하지 않으므로 비트수준 상수성을 만족해 컴파일되지만, 반환값이 가리키는 주소를 통해 외부에서 값을 수정할 수 있다. 이를 보완하기 위한 논리적 상수성은 일부 몇 비트 정도는 수정할 수 있되, 그것을 사용자 측에서 알아채지 못하면 된다는 개념이다.
(4) const 멤버 함수 안에서는 비트수준 상수성에 따라 멤버 변수를 수정할 수 없기 때문에 textLength와 lengthIsValid에 대입하는 부분에서 에러가 난다. 해결은 이 두 변수의 선언 앞에 mutable 키워드를 붙여 const의 제한에서 예외 대우를 받게 하는 것이다(mutable size_t textLength; mutable bool lengthIsValid;).
🔍 피드백:
- (1) 2.5/2.5 — 세 선언의 포인터/데이터 수정 가능 여부를 모두 정확히 답했습니다.
- (2) 2.5/2.5 —
it는*it수정 가능·++it불가,cit는*cit수정 불가·++cit가능으로 정확합니다. - (3) 2.5/2.5 — 비트수준 상수성의 정의, 내부 참조를 반환하면 규칙이 깨진다는 이유, 논리적 상수성의 정의를 모두 맞게 설명했습니다.
- (4) 2.5/2.5 — 에러 이유와 해결(
textLength,lengthIsValid선언 앞에mutable)이 정확합니다.
출처:
game_dev/cpp/chapter1/2025-04-06-cpp_1_3.md오답노트:wn-628f38c5· 2026-09-10 최초 오답 · 이번이 3번째 복습
📌 복습 포인트
- Q5
std::move와const—const객체에std::move를 써도 이동 생성자가 아니라 복사 생성자가 호출됨 →game_dev/moderncpp/5/2026-09-10-moderncpp_5_23.md
댓글남기기