Menu
Coddy logo textTech

Pimplイディオム

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 78/104。

Pimpl idiom(Pointer to Implementation)は、class の実装の詳細を別の forward-declared class に移すことで隠す技法です。これによりコンパイルの依存関係が減り、private メンバーを header ファイルから完全に隠すことができます。

核心となる考え方は単純です。class 内で private メンバーを直接 declare する代わりに、source file 内でのみ defined された implementation class へのポインターを declare します:

// 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をインクルードするすべてのファイルではありません。これにより、大規模なプロジェクトでのビルド時間が大幅に短縮されます。

unique_ptr がそれを削除するには Impl の完全な型が必要なため、destructor は source file 内で(defaulted であっても)defined されなければならないことに注意してください。これは、初めて Pimpl を使用するときによくある落とし穴です。

challenge icon

チャレンジ

簡単

Pimplイディオムを使用して安全なメッセージハンドラーを構築し、header fileからencryptionのimplementation detailsを隠しましょう。これにより、Pimplがcompilation firewallをどのように作成するかを示します。あなたのheaderをincludeする人は誰も、メッセージ処理の内部動作を見ることができません。

コードを3つのfileに整理します。

  • SecureMessage.hSecureMessage classのpublic interfaceをDefineします。

    classにはForward-declared Impl classと、それへのstd::unique_ptrを持たせます。public interfaceには次を含めます:

    • original message用のconst std::string&を受け取るconstructor
    • destructor(ここでdeclareし、.cpp fileでdefinedする必要があります)
    • encryption keyを設定するsetKey(int key) method
    • encrypted messageをstd::stringとして返すgetEncrypted() method
    • original messageを返すgetOriginal() method

    headerにはpublic interfaceのみを表示し、encryptionの動作方法に関するimplementation detailsはここに表示されないようにします。

  • SecureMessage.cpp:ネストされたImpl classをDefineし、すべてのmethodをImplementします。

    Impl classには、original message、encryption key(defaultは0)、および実際のencryption logicを格納させます。encryptionには単純なCaesar cipherを使用し、各characterをkey value分shiftします。例えば、keyが3の場合、'a'は'd'になり、'z'はwrap aroundして'c'になります。

    encryptionはlowercase letters(a-z)にのみ影響し、それ以外のすべてのcharacterは変更しないようにします。unique_ptrにはcompleteなImpl typeが必要なので、defaultedであってもdestructorをここでdefineすることを忘れないでください。

  • main.cpp:2つの入力を読み取ります:
    1. message string(spacesをcontainする場合があります)
    2. encryption key(integer)

    SecureMessage objectをCreateし、keyを設定して、結果を表示します:

    1. Original: に続けてoriginal messageをPrintします
    2. Encrypted: に続けてencrypted messageをPrintします

例えば、入力がhello world3の場合:

Original: hello world
Encrypted: khoor zruog

入力がxyz abc5の場合:

Original: xyz abc
Encrypted: cde fgh

header fileからCaesar cipherのimplementationについて何も明らかになっていないことに注目してください。これがPimplの力です。後で別のencryption algorithmに変更する場合、再コンパイルが必要なのはSecureMessage.cppのみであり、headerをincludeするfileは必要ありません。

自分で試してみよう

#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++オンラインコンパイラ