Menu
Coddy logo textTech

Pimpl 이디엄

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 78번째.

Pimpl idiom (Pointer to Implementation)는 class의 implementation 세부 정보를 별도의 Forward-declared class로 옮겨 숨기는 기법입니다. 이는 컴파일 종속성을 줄이고 private 멤버가 header 파일에서 진정으로 숨겨진 상태로 유지되도록 합니다.

핵심 아이디어는 간단합니다. class 안에서 private 멤버를 직접 선언하는 대신, source file에서만 정의된 implementation class에 대한 포인터를 선언합니다:

// Widget.h
#include <memory>

class Widget {
public:
    Widget();
    ~Widget();
    void doSomething();
    
private:
    class Impl;  // 전방 선언
    std::unique_ptr<Impl> pImpl;
};
// Widget.cpp
#include "Widget.h"
#include <iostream>

class Widget::Impl {
public:
    int data = 42;
    void process() { std::cout << "Processing: " << data << "\n"; }
};

Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;

void Widget::doSomething() { pImpl->process(); }

주요 이점은 컴파일 방화벽바이너리 호환성입니다. Impl 클래스를 변경하면 header를 포함하는 모든 파일이 아니라 소스 파일만 다시 컴파일하면 됩니다. 따라서 대규모 프로젝트에서 빌드 시간이 크게 단축됩니다.

destructor는 source file에서 defined되어야 합니다(심지어 defaulted인 경우에도). unique_ptr가 이를 삭제하려면 Impl의 complete type이 필요하기 때문입니다. 이는 Pimpl을 처음 사용할 때 흔히 발생하는 함정입니다.

challenge icon

챌린지

쉬움

Pimpl 관용구를 사용하여 header file에서 encryption 구현 세부 정보를 숨기는 secure message handler를 만들어 보겠습니다. 이는 Pimpl이 compilation firewall을 만드는 방식을 보여 줍니다. 즉, 여러분의 header를 include하는 사람은 누구도 message processing의 내부 동작을 볼 수 없습니다.

코드를 세 개의 file에 나누어 구성합니다.

  • SecureMessage.h: SecureMessage class의 public interface를 Define합니다.

    class에는 Forward 선언된 Impl class와 이를 가리키는 std::unique_ptr가 있어야 합니다. public interface에는 다음이 포함되어야 합니다.

    • original message를 위한 const std::string&를 받는 constructor
    • destructor (.cpp file에서 defined되어야 하며, 여기에서 declared되어야 함)
    • encryption key를 설정하는 setKey(int key) method
    • encrypted message를 std::string으로 반환하는 getEncrypted() method
    • original message를 반환하는 getOriginal() method

    header에는 public interface만 표시되어야 합니다. encryption이 어떻게 작동하는지에 대한 implementation 세부 정보는 여기에 표시되면 안 됩니다.

  • SecureMessage.cpp: 중첩된 Impl class를 Define하고 모든 method를 Implement합니다.

    Impl class는 original message, encryption key (default는 0)를 저장하고 실제 encryption logic을 처리해야 합니다. encryption에는 간단한 Caesar cipher를 사용합니다. 각 문자를 key 값만큼 shift합니다. 예를 들어 key가 3이면 'a'는 'd'가 되고, 'z'는 순환하여 'c'가 됩니다.

    encryption은 lowercase letters (a-z)에만 영향을 주어야 하며, 다른 모든 문자는 변경하지 않고 그대로 둡니다. unique_ptr에는 complete Impl type이 필요하므로, destructor가 defaulted된 경우에도 여기에서 Define해야 한다는 점을 기억하세요.

  • main.cpp: 두 개의 입력을 읽습니다.
    1. message string (spaces를 contain할 수 있음)
    2. encryption key (integer)

    SecureMessage object를 Create하고, key를 설정한 다음 결과를 표시합니다.

    1. Original: 을 Print한 followed by original message
    2. Encrypted: 를 Print한 followed by encrypted message

예를 들어, inputs가 hello world3인 경우:

Original: hello world
Encrypted: khoor zruog

inputs가 xyz abc5인 경우:

Original: xyz abc
Encrypted: cde fgh

header file에는 Caesar cipher implementation에 관한 내용이 전혀 드러나지 않는다는 점에 주목하세요. 이것이 Pimpl의 힘입니다. 나중에 다른 encryption algorithm으로 변경하더라도 header를 include하는 어떤 file도 아닌 SecureMessage.cpp만 recompilation하면 됩니다.

직접 해보기

#include <iostream>
#include <string>
#include "SecureMessage.h"

int main() {
    // 메시지 읽기 (공백 포함 가능)
    std::string message;
    std::getline(std::cin, message);
    
    // 암호화 키 읽기
    int key;
    std::cin >> key;
    
    // TODO: 메시지로 SecureMessage 객체 생성
    
    // TODO: 암호화 키 설정
    
    // TODO: "Original: " 다음에 원본 메시지 출력
    
    // TODO: "Encrypted: " 다음에 암호화된 메시지 출력
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C++ 컴파일러