我正在寻找一种简单的方法来减少C ++项目中的头部耦合,这主要是由于(过度使用)类组合,这当然需要完整的类型。例如:
// header A
class A
{
B b; // requires header B
};
我也考虑过接口和pimpl,但两者都暗示了一些我不想手动编写/支持的样板代码(或者有没有办法让它自动化?)。
所以我考虑用指针和前向替换成员class B* pB;
,但这需要处理对象的创建和删除。好的,我可以使用智能指针进行删除(不是auto_ptr
,因为它在创建时需要完整的类型,所以请说shared_ptr<class B> pB;
),但现在如何创建对象?
我可以在A
的构造函数中创建对象,如pB = new B;
,但这又是手动的,更糟糕的是,可能有几个构造函数......所以我在寻找有关自动执行此操作的方法,这可以像在B b;
的定义中将autoobjptr<class B> pB;
更改为A
一样简单,而无需费心pB
实例化。
我很确定这不是一个新想法,所以也许你可以给我一个共同解决方案或讨论的参考?
更新:为了澄清,我不打算打破A
和B
之间的依赖关系,但我想避免包含B
标题,其中一个包含A
的标题。在实践中,B
用于A
的实现,因此一个典型的解决方案是为A
创建一个接口或pimpl,但我现在正在寻找更容易的东西。
UPDATE2:我突然意识到一个懒惰的指针,例如建议的here会做的伎俩(太糟糕了,没有标准的实现,比如说升级),当与虚拟结合时析构函数(允许不完整类型)。我仍然不明白为什么没有标准的解决方案,并且想要重新发明轮子......
UPDATE3:突然之间,Sergey Tachenov带来了一个非常简单的解决方案(接受的答案),虽然我花了半个小时才明白为什么它真的有用......
如果删除A()构造函数或在头文件中将其定义为内联,魔法将不再起作用(compliation错误)。我想当你定义一个显式的非内联构造函数时,成员的构造(甚至是隐式构造)是在完成类型B
的同一编译单元(A.cpp)内完成的。另一方面,如果您的A
构造函数是内联的,则成员的创建必须在其他编译单元中进行,并且由于B
在那里不完整而无法工作。嗯,这是合乎逻辑的,但现在我很好奇 - 这个行为是由C ++标准定义的吗?
UPDATE4:希望最后更新。有关上述问题的讨论,请参阅接受的答案和评论。
答案 0 :(得分:3)
答案 1 :(得分:1)
我很确定这可以通过实现unique_ptr
的方式实现。不同之处在于allocated_unique_ptr
构造函数默认会分配B对象。
但是请注意,如果要自动构造B对象,将使用默认构造函数进行实例化。
答案 2 :(得分:0)
嗯,你自己给出了最好的解决方案,在构造函数中使用指针和new
......如果有多个构造函数,那么在那里重复那些代码。你可以创建一个为你做这个的基类,但这只会使实现神秘化......
您是否考虑过class B
中的模板?这也可以解决你的标题交叉依赖关系,但是可能会增加你的编译时间......这就是我们试图避免这些#include
的原因。你有没有测量编译时间?这令人不安吗?这是问题吗?
更新:模板方式示例:
// A.h
template<class T>
class A
{
public:
A(): p_t( new T ) {}
virtual ~A() { delete p_t }
private:
T* p_t;
};
同样,这很可能不会增加编译时间(B.h
需要被拉入以创建模板实例A<B>
),它确实可以让你删除A头中的包含和源文件。
答案 3 :(得分:0)
您可以编写一个自动pimpl_ptr<T>
,它会自动删除,删除和复制包含的T.
答案 4 :(得分:0)
您的方法存在的问题是,虽然您可以避免以这种方式包含B的头文件,但它并没有真正减少依赖性。
减少依赖关系的更好方法是让B派生自在单独的头文件中声明的基类,并在A中使用指向该基类的指针。您仍然需要手动创建正确的后代(B)当然,在A的构造函数中。
A和B之间的依赖关系也很可能是真实的,在这种情况下,你通过人工避免包含B的头文件来改善一切。