C/C++;API设计困境 我一直在分析C++中API设计的问题,以及如何在实现接口与语言分离时围绕语言中的一个大洞进行工作。
我是一个纯粹主义者,并且坚信将系统的公共界面与其实现相关的任何信息巧妙地分离开来。我每天都在一个巨大的代码库上工作,这个代码库不仅构建速度非常慢,主要是因为头文件拉了大量的其他头文件,而且作为客户机也非常难以挖掘某些东西的功能,因为该接口包含各种供公共、内部和私人使用的功能 我的库被分成几个层,每个层使用一些其他层。这是一种设计选择,向客户机公开每个级别,这样他们就可以通过使用较低级别的实体来扩展高级实体的功能,而无需使用我的存储库 现在问题来了。经过长时间的思考,我得出这样的结论:C++中没有任何方法可以将一个类的公共接口与细节分开,满足以下所有要求:C/C++;API设计困境 我一直在分析C++中API设计的问题,以及如何在实现接口与语言分离时围绕语言中的一个大洞进行工作。,c++,interface,pimpl-idiom,C++,Interface,Pimpl Idiom,我是一个纯粹主义者,并且坚信将系统的公共界面与其实现相关的任何信息巧妙地分离开来。我每天都在一个巨大的代码库上工作,这个代码库不仅构建速度非常慢,主要是因为头文件拉了大量的其他头文件,而且作为客户机也非常难以挖掘某些东西的功能,因为该接口包含各种供公共、内部和私人使用的功能 我的库被分成几个层,每个层使用一些其他层。这是一种设计选择,向客户机公开每个级别,这样他们就可以通过使用较低级别的实体来扩展高级实体的功能,而无需使用我的存储库 现在问题来了。经过长时间的思考,我得出这样的结论:C++中没有
据我所知,这是我的问题所在,在C++中有三种方法完全隐藏类从公共接口实现。
定义一个不透明的句柄到你的实体,一组函数,把这个句柄作为第一个参数,很好地满足了所有的要求,但它不是惯用的C++。 您仍然可以将其封装在类中。不透明句柄将是类的唯一私有数据成员,其实现不会以任何方式公开。在实现方面,它只是一个指向私有数据结构的指针,由类的成员函数取消引用。与C解决方案相比,这仍然是一个小小的改进,因为所有相关的数据和函数都将封装在一个类中,这使得客户端不必跟踪句柄并将其传递给每个函数
是的,我想取消对指针的引用会带来一些微不足道的开销,但是C解决方案也会有同样的问题不需要代码复制,尽管它可以被认为是黑客(或者至少是不优雅的C++设计),但它肯定不是C语言中实现的相同方法。唯一的区别是C程序员对于“黑客”的门槛较低,因为他们的语言表达方式少。 我正在考虑的设计的大致草图(基本上与PIMPL相同,但仅将数据成员设置为不透明):
//在实现文件中:
名称空间{
结构DrawingPenData
{
int厚度;
红色;
绿色;
蓝色;
//…描述对象或跟踪其状态所需的任何其他信息
};
}
//ctor、dtor、成员函数等的定义。
//例如:
空心绘图笔::设置厚度(内部厚度)
{
//通过句柄获取对象数据。
DrawingPenData*pData=重新解释(本->pPen);
//更新厚度。
pData->厚度=厚度;
}
如果您需要在
DrawingPen
上工作的私有函数,但不想在DrawingPen
头中公开这些函数,则只需将它们放在实现文件中相同的匿名命名空间中,接受对类对象的引用。我尽可能理解这个问题,“我是一个纯粹主义者,坚信将系统的公共界面与其实现相关的任何信息巧妙地分离开来。”而且“这是一个向客户公开每一层的设计选择”不能(对我而言)以任何形式组合在一起有意义的方式。啊,你说你想要一个<代码> C++ +>代码>解决方案,只要它不使用你在代码> C++ +>代码>中不习惯的技巧。虽然我没有得到推理。为什么要这样的限制?或者,澄清,没有理由更喜欢<代码> C <代码>超过<代码> C++ >代码>这里因为没有任何阻止。你在C++
中做了什么,你仍然可以在实际的实现代码中使用C++
比C
有很多优势。@ChristianHackl很抱歉,这还不清楚。我会尝试在这方面做更多的扩展,因为这并不矛盾。假设你正在编写一个渲染引擎。一个yer可能是图形API上的一个精简抽象。您肯定希望将实现隐藏在那里,因为它依赖于API。但该库还提供了更高级别的实体,使用此抽象来渲染内容,如网格、灯光等。它不只是将库锁定在高级别实体上,而是公开较低级别的实体
// In a header file:
class DrawingPen
{
public:
DrawingPen(...); // ctor
~DrawingPen(); // dtor
void SetThickness(int thickness);
// ...and other member functions
private:
void *pPen; // opaque handle to private data
};
// In an implementation file:
namespace {
struct DrawingPenData
{
int thickness;
int red;
int green;
int blue;
// ... whatever else you need to describe the object or track its state
};
}
// Definitions of the ctor, dtor, member functions, etc.
// For instance:
void DrawingPen::SetThickness(int thickness)
{
// Get the object data through the handle.
DrawingPenData *pData = reinterpret_cast<DrawingPenData*>(this->pPen);
// Update the thickness.
pData->thickness = thickness;
}