我正在开发一个库.我有从外部调用的接口类.
我还有一个不应该从外面调用的内部引擎.
当我在这里和那里阅读时,我应该隐藏内部引擎类,甚至不填充它的标题.由于我有以下结构:
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{};
这解决了这个问题.但是,从现在开始动态分配引擎.它的所有成员变量都在免费商店中.
我无法理解我必须改变我的设计并在免费商店上分配引擎来解决隐藏实现细节的问题.有更好的解决方案吗?
附:我不是在问这个解决方案为何有效.我知道如果我将它留在堆栈上,知道引擎类的大小是强制性的.我的问题是要求一个可以解决问题的不同设计.
编辑:
接口和引擎都有成员变量.
解决方法
你正在使用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; }
这里的缺点是你必须动态分配接口而不是引擎.
编辑:
基于STL容器和Howard Hinnant的stack allocator,在免费商店中避免变量的第三种方法可以是:
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); }