25. unique_ptr + 불완전 타입(Pimpl)에서 소멸자를 헤더에 기본으로 두면 안 되는 이유
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C++
// 문제 25 — unique_ptr + 불완전 타입(incomplete type)과 Pimpl 소멸자
//
// 아래는 Pimpl(Pointer to Implementation) 관용구로 작성된 게임 서버의
// PlayerSession 클래스다. 헤더/구현을 한 파일에 합쳐 두었지만, 실제로는
// PlayerSession.h / PlayerSession.cpp로 분리되어 있다고 가정하고 주석으로
// 그 경계를 표시했다.
//
// (A) 헤더 부분에서 소멸자를 "= default"로 선언(정의는 아직 안 함)했다.
// (B) Impl의 완전한 정의는 .cpp 파트에만 있다.
//
// 과제:
// 1. 아래 코드를 그대로 하나의 번역 단위(.cpp)로 컴파일하면 성공한다.
// 그런데 만약 (A)의 "~PlayerSession() = default;" 줄을 클래스 정의부(헤더 파트)
// 안으로 그대로 옮겨서 그 자리에서 "= default"로 정의해버리면(즉, .cpp 파트의
// "PlayerSession::~PlayerSession() = default;" 줄을 지우고 헤더에서 바로
// "~PlayerSession() = default;" 로 쓰면) 무슨 일이 벌어지는가?
// 컴파일 에러가 나는지, 난다면 어떤 메시지·어느 시점(헤더를 include하는 다른
// 번역 단위? 이 파일 자체?)에 나는지 예측하라.
// 2. 왜 unique_ptr<Impl> 을 멤버로 갖는 클래스는 소멸자(및 이동 연산자)를
// "헤더에서 선언만 하고 .cpp에서 정의"해야 하는지, unique_ptr의 삭제자가
// 인스턴스화되는 시점과 관련지어 설명하라.
// 3. 만약 Impl을 shared_ptr<Impl>로 바꾼다면 1번과 같은 문제가 여전히 발생하는지,
// 발생하지 않는다면 unique_ptr과 shared_ptr의 삭제자 처리 방식 차이로 설명하라.
#include <memory>
#include <string>
#include <vector>
// ===== PlayerSession.h (헤더 파트) =====
class PlayerSession {
public:
PlayerSession();
~PlayerSession(); // .cpp에서 정의됨 (아래 참고)
PlayerSession(PlayerSession&&) noexcept;
PlayerSession& operator=(PlayerSession&&) noexcept;
PlayerSession(const PlayerSession&) = delete;
PlayerSession& operator=(const PlayerSession&) = delete;
void OnRecv(const std::string& data);
private:
struct Impl; // 불완전 타입 (전방 선언만)
std::unique_ptr<Impl> impl_;
};
// ===== PlayerSession.cpp (구현 파트) =====
struct PlayerSession::Impl {
std::vector<std::string> pendingMessages;
int sessionId = 0;
void HandleRecv(const std::string& data) {
pendingMessages.push_back(data);
}
};
PlayerSession::PlayerSession() : impl_(std::make_unique<Impl>()) {}
// (A) 소멸자는 여기, Impl이 완전한 타입으로 보이는 지점에서 정의된다.
PlayerSession::~PlayerSession() = default;
PlayerSession::PlayerSession(PlayerSession&&) noexcept = default;
PlayerSession& PlayerSession::operator=(PlayerSession&&) noexcept = default;
void PlayerSession::OnRecv(const std::string& data) {
impl_->HandleRecv(data);
}
int main() {
PlayerSession session;
session.OnRecv("hello");
return 0;
} 내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.