Paylaşılan kitaplıklarda, pimpl deyiminin saf sanalların yapamayacağı kadar düzgün bir şekilde atlattığı çok gerçek bir sorun var: sınıfın kullanıcılarını kodlarını yeniden derlemeye zorlamadan bir sınıfın veri üyelerini güvenli bir şekilde değiştiremez / kaldıramazsınız. Bu, bazı durumlarda kabul edilebilir, ancak örneğin sistem kitaplıkları için geçerli değildir.
Sorunu ayrıntılı olarak açıklamak için, paylaşılan kitaplığınızda / başlığınızda aşağıdaki kodu göz önünde bulundurun:
// header
struct A
{
public:
A();
// more public interface, some of which uses the int below
private:
int a;
};
// library
A::A()
: a(0)
{}
Derleyici, işaretleyiciden olduğunu bildiği A nesnesine belirli bir uzaklık (bu durumda tek üye olduğu için muhtemelen sıfırdır) olarak başlatılacak tamsayının adresini hesaplayan paylaşılan kitaplıkta kod yayar this
.
Kod kullanıcı tarafında, bir new A
ilk tahsis edecek sizeof(A)
daha sonra bellek için bir işaretçi el, bellek bayt A::A()
olarak kurucu this
.
Kitaplığınızın daha sonraki bir revizyonunda tamsayıyı kaldırmaya, büyütmeye, küçültmeye veya üye eklemeye karar verirseniz, kullanıcının kodunun ayırdığı bellek miktarı ile yapıcı kodunun beklediği uzaklıklar arasında bir uyumsuzluk olacaktır. Muhtemel sonuç bir çökmedir, eğer şanslıysanız - daha az şanslıysanız, yazılımınız tuhaf davranır.
Pimpl'ing ile, paylaşılan kitaplıkta bellek ayırma ve yapıcı çağrısı gerçekleştiğinden, iç sınıfa veri üyelerini güvenle ekleyebilir ve kaldırabilirsiniz:
// header
struct A
{
public:
A();
// more public interface, all of which delegates to the impl
private:
void * impl;
};
// library
A::A()
: impl(new A_impl())
{}
Şimdi yapmanız gereken tek şey, genel arabiriminizi uygulama nesnesine yönelik işaretçi dışında veri üyelerinden uzak tutmaktır ve bu hata sınıfından kurtulursunuz.
Düzenleme: Burada kurucudan bahsetmemin tek sebebinin daha fazla kod sağlamak istememem olduğunu eklemeliyim - aynı argümantasyon veri üyelerine erişen tüm işlevler için geçerlidir.