通过减少填充头的数量来隐藏实现细节
hiding implementation details by reducing number of populated headers
我正在开发一个图书馆。我有一个可以从外部调用的接口类。
我也有一个内部引擎,不应该从外部调用。
当我在这里和那里阅读时,我应该隐藏内部引擎类,甚至不填充它的头。因为我有以下结构:
interface.hpp:
#include "engine.hpp"
class interface{
private:
enigne eng;
};
interface.cpp:
#include "engine.hpp"
//code that uses member variables and functions from eninge
engine.hpp:
class engine{};
为了解决填充"engine.hpp"的问题,我应该将代码更改为:
interface.hpp:
class engine;
class interface{
private:
some_smart_pointer<enigne> eng_ptr;
};
interface.cpp:
#include "engine.hpp"
//code that uses member variables and functions from eninge
enigne.hpp:
class engine{};
这就解决了问题。但是,从现在开始,engine
是动态分配的。它的所有成员变量都在自由存储库中。
我无法理解,为了解决隐藏实现细节的问题,我必须改变我的设计并在自由存储上分配引擎。有没有更好的解决方案?
注:我不是在问为什么这个解决方案有效。我知道这是关于知道引擎类的大小是强制性的,如果我把它放在堆栈上。我的问题是要求一个不同的设计来解决这个问题。
编辑: interface
和engine
都有成员变量
您正在使用PIMPL习语。另一种隐藏实现的方法是使用接口,即抽象基类和工厂函数:
interface.hpp:
class interface{
public:
virtual ~interface(){}
virtual void some_method() = 0;
};
// the factory function
static some_smart_pointer<interface> create();
interface.cpp:
#include "interface.hpp"
#include "engine.hpp"
class concrete : public interface{
public:
virtual void some_method() override { /* do something with engine */ }
private:
engine eng;
};
some_smart_pointer<interface> create(){ return new concrete; }
main.cpp
#include "interface.hpp"
int main()
{
auto interface = create();
interface->some_method();
return 0;
}
这里的缺点是必须动态地分配interface
而不是engine
。
关于PIMPL和接口的更多讨论在这里和这里
编辑:基于STL容器和Howard Hinnant的堆栈分配器,第三种避免自由存储中的变量的方法是:
interface.hpp:
class interface{
public:
interface();
~interface();
// I disable copy here, but you can implement them
interface(const interface&) = delete;
interface& operator=(interface&) = delete;
private:
engine* eng;
};
interface.cpp:
#include "interface.hpp"
#include "engine.hpp"
#include "short_alloc.h"
#include <map>
namespace
{
std::map<
interface*,
engine,
std::default_order<interface*>,
// use a stack allocator of 200 bytes
short_alloc<std::pair<interface*, engine>, 200>
> engines;
}
interface::interface():
eng(&engines[this])
// operator[] implicitly creates an instance and returns a reference to it
// the pointer gets the address
{
}
interface::~interface()
{
// destroy the instance
engines.erase(this);
}
我无法理解,为了解决隐藏实现细节的问题,我必须改变我的设计并在自由存储上分配引擎。
你好像已经自己解释过了:
我知道它是关于知道引擎类的大小
。如果interface
有engine
类型的成员,则必须知道该成员的大小,否则无法知道interface
本身的大小。不完整字型的大小是未知的。定义成员类型可以解决这个问题,但与隐藏实现的愿望相冲突。
有更好的解决方案吗?
没有更好的解决方案。带有免费存储的PIMPL——这是您目前使用的模式——是最好的。
从技术上讲,PIMPL不要求您使用免费存储。您可以将对象存储在您喜欢的任何地方。您可以使用静态存储。但这会限制实例的数量。你甚至可以分配一个内存缓冲区作为interface
的成员,但是你需要硬编码这个缓冲区的大小,它必须匹配engine
的大小(和对齐方式)。
我认为这两种理论建议都是拼凑的,尤其是后者。如果您的分析表明该特定额外分配的开销很大,并且不能选择放弃PIMPL,那么静态存储可能是值得的。
这是这个问题的标准解决方案:它被命名为PIMPL(指针到实现)习惯用法。顾名思义,它总是包含一个指向实现类的指针(或智能指针),因为c++根本不允许这样做:
class engine;
class interface{
private:
enigne eng;
};
隐藏实现和不强制对象在堆上分配的典型解决方案是将它们放在detail
命名空间中,并将这些类型的头文件放在详细子目录中。
请注意,您的实现的某些方面将被公开,因为它将影响库的ABI。没有一种解决方案可以既给你 ABI绝缘,又避免在堆上分配对象。
很可能你最好使用pimpl,正如你所描述的。
- 使用智能指针指向 C 库中的结构,该结构通过 typedef 隐藏实现(即不完整的类型)
- 我可以为 C++ 类提供不完整的标头以隐藏实现详细信息吗?
- 使用TDD时隐藏文件访问实现详细信息
- 隐藏变异模板实现
- 隐藏PIMPL-Objects拥有的成员的实现
- 隐藏C 接口的特定实现
- 在仅标头库中隐藏实现
- 使用指针(PIMPL IDIOM)隐藏实现
- 是否可以隐藏模板类的实现
- 从其接口隐藏类实现
- 使用私有继承来隐藏实现是个好主意吗
- 如何在C++共享库中隐藏业务对象的实现细节并提供接口
- 通过前向声明隐藏实现
- 扩展类以进行调试:公共API、隐藏实现或其他什么
- 通过内部指针隐藏实现详细信息
- 循环包含在c++头文件中隐藏实现细节的技巧
- 通过减少填充头的数量来隐藏实现细节
- 从工厂函数返回 std::unique_ptr<T> 创建纯虚拟接口的完全隐藏实现
- 成员函数的隐藏实现(如静态全局函数)
- 如何隐藏实现助手模板