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 を使用するときによくある落とし穴です。
チャレンジ
簡単Pimplイディオムを使用して安全なメッセージハンドラーを構築し、header fileからencryptionのimplementation detailsを隠しましょう。これにより、Pimplがcompilation firewallをどのように作成するかを示します。あなたのheaderをincludeする人は誰も、メッセージ処理の内部動作を見ることができません。
コードを3つのfileに整理します。
SecureMessage.h:SecureMessageclassのpublic interfaceをDefineします。classにはForward-declared
Implclassと、それへの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はここに表示されないようにします。
- original message用の
SecureMessage.cpp:ネストされたImplclassをDefineし、すべてのmethodをImplementします。Implclassには、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なImpltypeが必要なので、defaultedであってもdestructorをここでdefineすることを忘れないでください。main.cpp:2つの入力を読み取ります:- message string(spacesをcontainする場合があります)
- encryption key(integer)
SecureMessageobjectをCreateし、keyを設定して、結果を表示します:Original:に続けてoriginal messageをPrintしますEncrypted:に続けてencrypted messageをPrintします
例えば、入力がhello worldと3の場合:
Original: hello world
Encrypted: khoor zruog入力がxyz abcと5の場合:
Original: xyz abc
Encrypted: cde fghheader 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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ