y.hpp:
#ifndef Y_HPP #define Y_HPP // LOTS OF FILES INCLUDED template <class T> class Y { public: T z; // LOTS OF STUFF HERE }; #endif
现在,我们希望能够在我们创建的类(比如X)中使用Y.但是,我们不希望X的用户必须包含Y标头.
所以我们定义了一个类X,如下所示:
x.hpp:
#ifndef X_HPP #define X_HPP template <class T> class Y; class X { public: ~X(); void some_method(int blah); private: Y<int>* y_; }; #endif
请注意,因为y_是指针,所以我们不需要包含它的实现.
实现在x.cpp中,它是单独编译的:
x.cpp:
#include "x.hpp" #include "y.hpp" X::~X() { delete y_; } void X::someMethod(int blah) { y_->z = blah; }
所以现在我们的客户可以只包含“x.hpp”来使用X,而不包括并且必须处理所有“y.hpp”标题:
main.cpp中:
#include "x.hpp" int main() { X x; x.blah(42); return 0; }
现在我们可以分别编译main.cpp和x.cpp,并且在编译main.cpp时我不需要包含y.hpp.
所以这是我的问题:
(1)有没有办法让Y成为X的直接成员(不是Y的指针),而不需要包含Y标题? (我强烈怀疑这个问题的答案是否定的)
(2)有没有办法可以使用智能指针类来处理堆分配的Y? unique_ptr似乎是显而易见的选择,但是当我更改x.hpp中的行时
从:
Y<int>* y_;
至:
std::unique_ptr< Y<int> > y_;
并包含,并用c 0x模式编译,我得到错误:
/usr/include/c++/4.4/bits/unique_ptr.h:64: error: invalid application of ‘sizeof’ to incomplete type ‘Y<int>’ /usr/include/c++/4.4/bits/unique_ptr.h:62: error: static assertion Failed: "can't delete pointer to incomplete type"
那么无论如何通过使用标准智能指针而不是原始指针以及自定义析构函数中的原始删除来执行此操作?
解:
Howard Hinnant说得对,我们需要做的就是以下列方式更改x.hpp和x.cpp:
x.hpp:
#ifndef X_HPP #define X_HPP #include <memory> template <class T> class Y; class X { public: X(); // ADD CONSTRUCTOR FOR X(); ~X(); void some_method(int blah); private: std::unique_ptr< Y<int> > y_; }; #endif
x.cpp:
#include "x.hpp" #include "y.hpp" X::X() : y_(new Y<int>()) {} // ADD CONSTRUCTOR FOR X(); X::~X() {} void X::someMethod(int blah) { y_->z = blah; }
我们很高兴使用unique_ptr.谢谢霍华德!
解决方案背后的理由:
如果我错了,人们可以纠正我,但是这个代码的问题是隐式默认构造函数试图默认初始化Y,并且因为它不知道关于Y的任何信息,所以它不能这样做.通过明确地说我们将在其他地方定义构造函数,编译器认为“好吧,我不必担心构造Y,因为它是在其他地方编译的”.
解决方法
shared_ptr和unique_ptr都可以防止您在不完整类型上意外调用delete.这使它们优于原始指针,它不提供这种保护. unique_ptr需要X()概述的原因可归结为它具有静态删除器而不是动态删除器.
编辑:更深入的澄清
由于unique_ptr和shared_ptr的静态删除器与动态删除器的区别,两个智能指针要求element_type在不同的位置完成.
的unique_ptr< a取代;要求A完成:
> ~unique_ptr< A>();
但不适用于:
> unique_ptr< A>();
> unique_ptr< A>(A *);
的shared_ptr< a取代;要求A完成:
> shared_ptr< A>(A *);
但不适用于:
> shared_ptr< A>();
> ~shared_ptr< A>();
最后,隐式生成的X()ctor将调用智能指针默认ctor和智能指针dtor(如果X()抛出异常 – 即使我们知道它不会).
结论:必须在元素类型完整的源中概述调用要求完成element_type的智能指针成员的X的任何成员.
unique_ptr和shared_ptr的好处在于,如果你猜错了需要概述的内容,或者如果你没有意识到某个特殊成员是隐式生成的,需要一个完整的element_type,这些智能指针会告诉你一个(有时措辞不好)编译时间错误.