Widzę dużo kodu źródłowego używającego idiomu PImpl w C ++. Zakładam, że jego celem jest ukrycie prywatnych danych / typu / implementacji, aby mógł on usunąć zależność, a następnie skrócić czas kompilacji i problem z nagłówkiem.
Ale klasy interfejsu / czysto abstrakcyjne w C ++ również mają tę możliwość, można ich również użyć do ukrycia danych / typu / implementacji. Aby wywołujący mógł widzieć interfejs podczas tworzenia obiektu, możemy zadeklarować metodę fabryczną w nagłówku interfejsu.
Porównanie jest następujące:
Koszt :
Koszt sposobu interfejsu jest niższy, ponieważ nie trzeba nawet powtarzać publicznej implementacji funkcji opakowania
void Bar::doWork() { return m_impl->doWork(); }
, wystarczy zdefiniować podpis w interfejsie.Dobrze zrozumiany :
Technologia interfejsu jest lepiej rozumiana przez każdego programistę C ++.
Wydajność :
Wydajność interfejsu nie gorsza niż idiom PImpl, zarówno dodatkowy dostęp do pamięci. Zakładam, że wydajność jest taka sama.
Poniżej znajduje się pseudokod ilustrujący moje pytanie:
// Forward declaration can help you avoid include BarImpl header, and those included in BarImpl header.
class BarImpl;
class Bar
{
public:
// public functions
void doWork();
private:
// You don't need to compile Bar.cpp after changing the implementation in BarImpl.cpp
BarImpl* m_impl;
};
Ten sam cel można zrealizować za pomocą interfejsu:
// Bar.h
class IBar
{
public:
virtual ~IBar(){}
// public functions
virtual void doWork() = 0;
};
// to only expose the interface instead of class name to caller
IBar* createObject();
Jaki jest sens PImpl?
źródło
Impl
klasie nie spowoduje przerwaniaABI
, ale dodanie funkcji publicznej w interfejsie ulegnie awariiABI
.unordered_set
), bez PImpl, muszę#include <unordered_set>
sięMyClass.h
. W przypadku PImpl muszę tylko dołączyć go do.cpp
pliku, a nie do.h
pliku, a zatem wszystko, co go zawiera ...Chcę tylko odnieść się do twojego punktu wydajności. Jeśli korzystasz z interfejsu, musisz koniecznie stworzyć funkcje wirtualne, które NIE zostaną wprowadzone przez optymalizator kompilatora. Funkcje PIMPL można (i prawdopodobnie będą, ponieważ są tak krótkie) wstawiać (być może więcej niż jeden raz, jeśli funkcja IMPL jest również mała). Wirtualnego wywołania funkcji nie można zoptymalizować, chyba że użyjesz pełnej optymalizacji analizy programu, co zajmuje bardzo dużo czasu.
Jeśli Twoja klasa PIMPL nie jest używana w sposób krytyczny dla wydajności, ten punkt nie ma większego znaczenia, ale twoje założenie, że wydajność jest taka sama, obowiązuje tylko w niektórych sytuacjach, nie we wszystkich.
źródło
Ta strona odpowiada na twoje pytanie. Wiele osób zgadza się z twoim twierdzeniem. Korzystanie z Pimpl ma następujące zalety w stosunku do prywatnych pól / członków.
Idiom Pimpl to optymalizacja czasu kompilacji, technika łamania zależności. Tnie duże pliki nagłówkowe. Zmniejsza to nagłówek tylko do interfejsu publicznego. Długość nagłówka jest ważna w C ++, gdy myślisz o tym, jak to działa. #include skutecznie łączy plik nagłówkowy z plikiem źródłowym - należy więc pamiętać, że wstępnie przetworzone jednostki C ++ mogą być bardzo duże. Korzystanie z Pimpl może poprawić czasy kompilacji.
Istnieją inne lepsze techniki przełamywania zależności - które wymagają ulepszenia projektu. Co oznaczają metody prywatne? Jaka jest jedyna odpowiedzialność? Czy powinni być inną klasą?
źródło