Bence Derived_1::Impl
türetmek kötü bir strateji Base::Impl
.
Pimpl deyimini kullanmanın temel amacı, bir sınıfın uygulama ayrıntılarını gizlemektir. İzin vererek Derived_1::Impl
kaynaklanıyor Base::Impl
, bu amaca yendiniz. Şimdi, sadece uygulamasına Base
bağlı olmakla kalmıyor, aynı zamanda Base::Impl
uygulamasına Derived_1
da bağlı Base::Impl
.
Daha iyi bir çözüm var mı?
Bu, hangi takasların sizin için kabul edilebilir olduğuna bağlıdır.
Çözüm 1
Marka Impl
sınıfları tamamen bağımsız. Bu iki işaretçi olacağını ima edecek Impl
tek - sınıflar Base
ve başka bir Derived_N
.
class Base {
protected:
Base() : pImpl{new Impl()} {}
private:
// It's own Impl class and pointer.
class Impl { };
std::shared_ptr<Impl> pImpl;
};
class Derived_1 final : public Base {
public:
Derived_1() : Base(), pImpl{new Impl()} {}
void func_1() const;
private:
// It's own Impl class and pointer.
class Impl { };
std::shared_ptr<Impl> pImpl;
};
Çözüm 2
Sınıfları yalnızca tanıtıcı olarak gösterin. Sınıf tanımlarını ve uygulamalarını hiçbir şekilde sergilemeyin.
Genel başlık dosyası:
struct Handle {unsigned long id;};
struct Derived1_tag {};
struct Derived2_tag {};
Handle constructObject(Derived1_tag tag);
Handle constructObject(Derived2_tag tag);
void deleteObject(Handle h);
void fun(Handle h, Derived1_tag tag);
void bar(Handle h, Derived2_tag tag);
İşte hızlı uygulama
#include <map>
class Base
{
public:
virtual ~Base() {}
};
class Derived1 : public Base
{
};
class Derived2 : public Base
{
};
namespace Base_Impl
{
struct CompareHandle
{
bool operator()(Handle h1, Handle h2) const
{
return (h1.id < h2.id);
}
};
using ObjectMap = std::map<Handle, Base*, CompareHandle>;
ObjectMap& getObjectMap()
{
static ObjectMap theMap;
return theMap;
}
unsigned long getNextID()
{
static unsigned id = 0;
return ++id;
}
Handle getHandle(Base* obj)
{
auto id = getNextID();
Handle h{id};
getObjectMap()[h] = obj;
return h;
}
Base* getObject(Handle h)
{
return getObjectMap()[h];
}
template <typename Der>
Der* getObject(Handle h)
{
return dynamic_cast<Der*>(getObject(h));
}
};
using namespace Base_Impl;
Handle constructObject(Derived1_tag tag)
{
// Construct an object of type Derived1
Derived1* obj = new Derived1;
// Get a handle to the object and return it.
return getHandle(obj);
}
Handle constructObject(Derived2_tag tag)
{
// Construct an object of type Derived2
Derived2* obj = new Derived2;
// Get a handle to the object and return it.
return getHandle(obj);
}
void deleteObject(Handle h)
{
// Get a pointer to Base given the Handle.
//
Base* obj = getObject(h);
// Remove it from the map.
// Delete the object.
if ( obj != nullptr )
{
getObjectMap().erase(h);
delete obj;
}
}
void fun(Handle h, Derived1_tag tag)
{
// Get a pointer to Derived1 given the Handle.
Derived1* obj = getObject<Derived1>(h);
if ( obj == nullptr )
{
// Problem.
// Decide how to deal with it.
return;
}
// Use obj
}
void bar(Handle h, Derived2_tag tag)
{
Derived2* obj = getObject<Derived2>(h);
if ( obj == nullptr )
{
// Problem.
// Decide how to deal with it.
return;
}
// Use obj
}
Lehte ve aleyhte olanlar
İlk yaklaşımla, Derived
yığın içinde sınıflar oluşturabilirsiniz. İkinci yaklaşımla, bu bir seçenek değildir.
İlk yaklaşımla, Derived
yığındaki bir inşası ve imhası için iki dinamik ayırma ve ayırma maliyetine katlanırsınız . Bir Derived
nesneyi öbekten inşa edip yok ederseniz, bir tahsis ve yeniden yerleştirmenin maliyetine katlanın. İkinci yaklaşımla, her nesne için yalnızca bir dinamik ayırma ve bir ayırma maliyetine katlanırsınız.
İlk yaklaşımla virtual
üye fonksiyonunu kullanabilme yeteneğine sahip olursunuz Base
. İkinci yaklaşımla, bu bir seçenek değildir.
Benim önerim
Sınıf hiyerarşisini ve virtual
üye işlevlerini Base
biraz daha pahalı olmasına rağmen kullanabilmem için ilk çözümle birlikte giderdim .
Base
normal bir temel sınıf ("arayüz") ve sivilce içermeyen somut uygulamalar yeterli olabilir.