我认为这是一个贫穷的战略,使Derived_1::Impl
从派生Base::Impl
。
使用Pimpl习惯用法的主要目的是隐藏类的实现细节。通过Derived_1::Impl
从派生Base::Impl
,您已经击败了这个目的。现在,不仅实现Base
依赖于Base::Impl
,实现Derived_1
也依赖于Base::Impl
。
有更好的解决方案吗?
这取决于您可以接受哪些折衷。
解决方案1
使Impl
类完全独立。这意味着将有两个指向Impl
类的指针-一个指针Base
和一个另一个指针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;
};
解决方案2
仅将类公开为手柄。根本不要公开类的定义和实现。
公开头文件:
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);
快速实施
#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
}
利弊
使用第一种方法,您可以Derived
在堆栈中构造类。对于第二种方法,这不是一种选择。
采用第一种方法时,您需要Derived
在栈中构造和销毁两个动态分配和释放的成本。如果您Derived
从堆中构造和销毁一个对象,则将产生分配和释放的费用。使用第二种方法,您只需为每个对象花费一次动态分配和一次重新分配的成本。
通过第一种方法,您可以使用virtual
成员函数is Base
。对于第二种方法,这不是一种选择。
我的建议
我将采用第一个解决方案,因此即使它稍微贵一点virtual
,Base
也可以使用类层次结构和成员函数。
Base
,一个普通的抽象基类(“接口”)和没有pimpl的具体实现可能就足够了。