Cli*_*ton 6 c++ pointers smart-pointers raii c++11
让我们说具有如下通用代码:
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
Run Code Online (Sandbox Code Playgroud)
现在,我们希望能够在我们创建的类(比如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
Run Code Online (Sandbox Code Playgroud)
请注意,因为y_是指针,所以我们不需要包含它的实现.
实现在x.cpp中,它是单独编译的:
x.cpp:
#include "x.hpp"
#include "y.hpp"
X::~X() { delete y_; }
void X::someMethod(int blah) { y_->z = blah; }
Run Code Online (Sandbox Code Playgroud)
所以现在我们的客户可以只包含"x.hpp"来使用X,而不包括并且必须处理所有"y.hpp"标题:
main.cpp:
#include "x.hpp"
int main()
{
X x;
x.blah(42);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以单独编译main.cpp,x.cpp编译时main.cpp我不需要包含y.hpp.
但是使用这个代码我必须使用原始指针,而且,我必须使用删除.
所以这是我的问题:
(1)有没有办法让Y成为X的直接成员(不是Y的指针),而不需要包含Y标题?(我强烈怀疑这个问题的答案是否定的)
(2)有没有办法可以使用智能指针类来处理堆分配的Y?unique_ptr似乎是显而易见的选择,但当我更改线路时x.hpp
从:
Y<int>* y_;
Run Code Online (Sandbox Code Playgroud)
至:
std::unique_ptr< Y<int> > y_;
Run Code Online (Sandbox Code Playgroud)
并包含,并用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"
Run Code Online (Sandbox Code Playgroud)
那么无论如何通过使用标准智能指针而不是原始指针以及自定义析构函数中的原始删除来执行此操作?
解:
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
Run Code Online (Sandbox Code Playgroud)
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; }
Run Code Online (Sandbox Code Playgroud)
我们很高兴使用unique_ptr.谢谢霍华德!
解决方案背后的理由:
如果我错了,人们可以纠正我,但是这个代码的问题是隐式默认构造函数试图默认初始化Y,并且因为它不知道关于Y的任何信息,所以它不能这样做.通过明确地说我们将在其他地方定义构造函数,编译器认为"好吧,我不必担心构造Y,因为它是在其他地方编译的".
真的,我应该首先添加一个构造函数,我的程序是没有它的bug.
How*_*ant 12
您可以使用unique_ptr或shared_ptr处理不完整类型.如果您使用shared_ptr,则必须~X()按照您的要求进行概述.如果你使用,unique_ptr你必须勾勒出两者~X()和X()(或者你用来构造的任何构造函数X).隐含生成的默认ctor X要求完整类型Y<int>.
双方shared_ptr并unique_ptr在保护你无意调用删除一个不完整的类型.这使它们优于原始指针,它不提供这种保护.原因unique_ptr要求X()归结为它具有静态删除而不是动态删除的事实.
编辑:更深入的澄清
因为静态缺失者VS的动力缺失者的差异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()抛出异常 - 即使我们知道它不会).
底线:任何需要完成X的智能指针成员的成员element_type必须概述到完成的源element_type.
约在很酷的事情unique_ptr,并shared_ptr是,如果你猜错了就什么需要概述,或者如果你没有意识到被隐式生成一个特殊的成员,需要一个完整的element_type,这些智能指针会告诉你一个(有时措辞不当)编译时错误.
1)你是对的,答案是"不":编译器应该知道成员对象的大小,如果没有Y类型的定义就不能知道它.
2)boost::shared_ptr(或tr1::shared_ptr)不需要完整类型的对象.因此,如果您能承受它所隐含的开销,那将有助于:
类模板在T上进行参数化,T是指向的对象的类型.shared_ptr及其大多数成员函数对T没有要求; 它被允许是一个不完整的类型,或无效.
编辑:已检查过unique_ptr文档.似乎你可以使用它:只需确保~X()定义unique_ptr<>构造的位置.