Arayüz C ++ 'da aynı amaç için kullanılabilirken PImpl paterninin amacı nedir?


49

C ++ 'ta PImpl deyimini kullanan bir çok kaynak kod görüyorum. Amacının, özel veri / tip / uygulama gizlemektir, böylece bağımlılığı ortadan kaldırabilir ve daha sonra derleme süresini azaltabilir ve başlık dahil sorunu.

Ancak C ++ 'ta arabirim / saf-soyut sınıfları da bu kabiliyete sahiptir, ayrıca veri / tür / uygulamayı gizlemek için de kullanılabilir. Ve bir nesnenin oluşturulmasında arayanın sadece arayüzü görmesine izin vermek için, arayüzün başlığında bir fabrika metodu ilan edebiliriz.

Karşılaştırma:

  1. Maliyet :

    Arayüz yolu maliyeti daha düşüktür, çünkü genel sarmalayıcı işlevi uygulamasını tekrarlamanıza gerek kalmaz void Bar::doWork() { return m_impl->doWork(); }, sadece arayüzdeki imzayı tanımlamanız gerekir.

  2. İyi anlaşıldı :

    Arayüz teknolojisi her C ++ geliştiricisi tarafından daha iyi anlaşılmaktadır.

  3. Performans :

    Arayüz yolu performansı PImpl deyiminden daha kötü değil, her ikisi de ekstra bellek erişimi. Performansın aynı olduğunu varsayıyorum.

Sorumu açıklamak için sözde kod:

// 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;
};

Aynı amaç arabirim kullanılarak da uygulanabilir:

// 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();

Peki PImpl'ın amacı nedir?

Yanıtlar:


50

İlk olarak, PImpl genellikle polimorfik olmayan sınıflar için kullanılır. Bir polimorfik sınıf PImpl'a sahip olduğunda, genellikle polimorfik kalır, bu hala arayüzleri uygular ve temel sınıftan vb. Sanal yöntemleri geçersiz kılar. Yani PImpl'ın daha basit bir uygulaması arayüz değildir , doğrudan üyeleri içeren basit bir sınıftır!

PImpl kullanmak için üç neden vardır:

  1. Yapımı ikili özel üyelerin arabirimi (ABI) bağımsız. Paylaşılan bir kütüphaneyi bağımlı kodu tekrar derlemeden güncellemek mümkündür, ancak sadece ikili arayüz aynı kaldığı sürece . Artık üye olmayan bir işlev eklemek ve sanal olmayan bir üye işlevi eklemek dışında başlığındaki hemen hemen her değişiklik ABI'yi değiştiriyor. PImpl deyimi özel üyelerin tanımını kaynağa taşır ve böylece ABI'yi tanımlarından ayırır. Kırılgan İkili Arayüz Problemine Bakın

  2. Bir başlık değiştiğinde, içerdiği tüm kaynakların yeniden derlenmesi gerekir. Ve C ++ derlemesi oldukça yavaştır. Dolayısıyla, özel üyelerin tanımlarını kaynağa taşıyarak, PImpl deyimi derleme süresini azaltır, çünkü başlıkta daha az bağımlılığın çekilmesi gerekir ve bağımlıların yeniden derlenmesi gerekmediğinden değişikliklerden sonra derleme süresini azaltır (Tamam, bu arayüz de gizli beton sınıfı ile birlikte arabirim + fabrika işlevi için geçerlidir).

  3. C ++ istisnasındaki birçok sınıf için güvenlik önemli bir özelliktir. Çoğunlukla birden fazla üye atarsa ​​işlem sırasında, hiçbir üye değiştirilmez veya üyeyi atarsa ​​tutarsız durumda bırakacak ve içerecek nesneyi kalmaya zorlayacak bir işlem yapmazsanız, bir grupta birkaç sınıf oluşturmanız gerekir. tutarlı, istikrarlı. Bu durumda, işlemi PImpl'nin yeni bir örneğini oluşturarak gerçekleştirir ve işlem başarılı olduğunda bunları değiştirirsiniz.

Aslında arayüz sadece uygulama gizleme için de kullanılabilir, ancak aşağıdaki dezavantajlara sahiptir:

  1. Sanal olmayan bir yöntem eklemek ABI'yi bozmaz, ancak sanal bir yöntem ekler. PImpl, arayüzler bu nedenle hiçbir yöntem eklemeye izin vermiyor.

  2. Inteface yalnızca işaretçi / başvuru yoluyla kullanılabilir, bu nedenle kullanıcının uygun kaynak yönetimine dikkat etmesi gerekir. Diğer yandan, PImpl kullanan sınıflar hala değer türleridir ve kaynakları dahili olarak ele alırlar.

  3. Gizli uygulama devralınamaz, PImpl ile sınıf yapabilirsiniz.

Ve tabii ki arayüz istisna güvenlik ile yardımcı olmaz. Bunun için sınıf içindeki dolaylılığa ihtiyacınız var.


İyi bir nokta. Kamu işlevi ekle Implkırmayacak sınıfına ABIfakat kıracak arayüzde kamu işlevi ekleyin ABI.
ZijingWu

1
Bir genel fonksiyon / metot ekleme yok olması ABI kırmak; kesinlikle katkı değişiklikleri değişiklikleri kırmaz. Ancak, hiç bu kadar dikkatli olmalısınız; Ön uç ile arka uç arasında aracılık eden kod, versiyonlama ile ilgili her türlü eğlence ile başa çıkmak zorundadır. Her şey zorlaşıyor. (Bu tür bir şey neden bir dizi yazılım projesinin C'yi C ++ 'ı tercih ettiğini; ABI'nin ne olduğunu tam olarak kavramalarını sağlıyor.)
Donal Fellows

3
@DonalFellows: bir genel fonksiyon / ekleme yöntemi değildir ABI bölünürler. Bir ekleme , sanal bir yöntem yapar ve yapar her kullanıcı (Pimpl elde) nesne miras önlenmektedir edilmiştir.
Jan Hudec

4
Sanal bir yöntem eklemenin neden ABI'yi ihlal ettiğini açıklamak için. Bağlantı ile ilgili olmalı. Sanal olmayan yöntemlere bağlantı, kitaplığın statik veya dinamik olmasına bakılmaksızın, ada göre yapılır. Başka bir sanal olmayan yöntem eklemek, bağlantı kurulacak başka bir ad eklemektir. Bununla birlikte, sanal yöntemler vtable'daki endekslerdir ve harici kodlar indeksi etkin bir şekilde bağlar. Sanal bir yöntem eklemek, dış kodun bilmesi için başka yöntemlerle vtable'da dolaşabilir.
Saat

1
PImpl ayrıca ilk derleme süresini de azaltır. Benim uygulama bazı şablon türünde bir alan varsa, örneğin, (örneğin unordered_set), Pimpl olmadan, ben gerek #include <unordered_set>içinde MyClass.h. Pimpl ile, ben sadece eklemenize gerek .cpp, dosyaya değil .hdosyası ve ... bunu içeren başka nedenle her şeyi
Martin C. Martin

7

Sadece performans noktanıza değinmek istiyorum. Bir arabirim kullanıyorsanız, derleyicinin eniyileyici tarafından satır içi çizilmeyecek olan sanal işlevler oluşturdunuz. PIMPL işlevleri (ve muhtemelen olacaktır, çünkü çok kısa oldukları için) (IMPL işlevi de küçükse belki birden fazla kez) sıralanabilir. Yapmanız çok uzun süren tam program analiz optimizasyonlarını kullanmadığınız sürece sanal fonksiyon çağrısı optimize edilemez.

PIMPL sınıfınız, performans açısından kritik bir şekilde kullanılmıyorsa, bu nokta çok da önemli değildir, ancak performansın aynı olduğu varsayımı, yalnızca bazı durumlarda geçerlidir, hepsinde değil.


1
Bağlantı zamanı optimizasyonunun kullanılmasıyla, pimpl deyiminin kullanılmasının genel giderlerinin çoğu kaldırılmıştır. Hem dış sarıcı işlevlerinin hem de uygulama işlevlerinin kullanımı, satır başlığına uygulanan normal bir sınıf gibi işlev çağrıları etkin bir şekilde yapılmasını sağlayarak sıralanabilir.
Goji

Bu, yalnızca bağlantı zamanı optimizasyonu kullanıyorsanız geçerlidir. PImpl'ın amacı, derleyicinin iletme işlevinin bile uygulaması olmadığını. Bağlayıcı olsa da.
Martin C. Martin

5

Bu sayfa sorunuzu yanıtlar. Birçok kişi iddiasına katılıyor. Pimpl kullanmak, özel alanlara / üyelere göre aşağıdaki avantajlara sahiptir.

  1. Bir sınıfın özel üye değişkenlerini değiştirmek, ona bağlı sınıfları yeniden derlemeyi gerektirmez, bu nedenle zamanları daha hızlı hale getirir ve FragileBinaryInterfaceProblem'i azaltılır.
  2. Başlık dosyasının özel üye değişkenlerinde 'değere göre' kullanılan sınıfları içermesi gerekmez, bu nedenle derleme zamanları daha hızlıdır.

Pimpl deyimi bir derleme zamanı optimizasyonu, bağımlılık kırma tekniğidir. Büyük başlık dosyalarını keser. Başlığınızı yalnızca genel arayüze indirger. Başlık uzunluğu, nasıl çalıştığını düşündüğünüzde C ++ 'ta önemlidir. #include, üstbilgi dosyasını kaynak dosyayla etkin bir şekilde birleştirir; bu nedenle önceden işlenmiş C ++ birimlerinin çok büyük olabileceğinden emin olun. Pimpl kullanımı derleme sürelerini iyileştirebilir.

Tasarımınızı geliştirmeyi içeren daha iyi bağımlılık kırma teknikleri var. Özel yöntemler neyi temsil ediyor? Tek sorumluluk nedir? Başka bir sınıf mı olmalılar?


PImpl, çalışma zamanı açısından hiç bir optimizasyon değildir. Tahsisler önemsiz değildir ve pimpl ilave tahsis anlamına gelir. Bağımlılık kırma tekniği ve sadece derleme süresinin optimizasyonu .
Jan Hudec

@ JanHudec açıklığa kavuşturuldu.
Dave Hillier

@DaveHillier, +1 denemek için FragileBinaryInterfaceProblem
ZijingWu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.