猿问

std:UNIQUE_PTR<T>是否需要知道T的完整定义?

std:UNIQUE_PTR<T>是否需要知道T的完整定义?

标题中有一些代码如下所示:

#include <memory>class Thing;class MyClass{
    std::unique_ptr< Thing > my_thing;};

如果我将此标头包含在不包括Thing类型定义,那么这不会在VS2010-SP1下编译:

1>C:\ProgramFiles(X86)\MicrosoftVisualStudio 10.0\VC\include(2067):错误C 2027:使用未定义类型的“Thing”

取代std::unique_ptr通过std::shared_ptr它会编译。

所以,我猜是现在的VS 2010std::unique_ptr它的实现需要完整的定义,并且它完全依赖于实现。

或者是?在它的标准要求中有什么东西是不可能的吗?std::unique_ptr它的实现只适用于前向声明?这感觉很奇怪,因为它应该只包含指向Thing不是吗?


繁星coding
浏览 980回答 3
3回答

梦里花落0921

采自这里.C+标准库中的大多数模板都要求用完整类型实例化它们。不过shared_ptr和unique_ptr是部分例外。一些成员(但不是所有成员)可以用不完整类型实例化。这样做的动机是支持这样的成语,例如PIMPL使用智能指针,并且不冒未定义行为的风险。当您有一个不完整的类型并且您调用时,可能会发生未定义的行为。delete在上面:class&nbsp;A;A*&nbsp;a&nbsp;=&nbsp;...;delete&nbsp;a;以上是法律法规。它将汇编。您的编译器可能对上述代码发出类似上述代码的警告,也可能不会发出警告。当它执行时,坏的事情可能会发生。如果你很幸运你的程序会崩溃。然而,更有可能的结果是,您的程序将悄悄地泄漏内存,作为~A()不会被打电话的。使用auto_ptr<A>在上面的例子中没有帮助。您仍然会得到相同的未定义行为,就像使用了原始指针一样。然而,在某些地方使用不完整的类是非常有用的!这里是shared_ptr和unique_ptr帮助。使用这些智能指针之一将使您可以避免使用不完整类型,除非需要使用完整类型。最重要的是,当需要有一个完整的类型时,如果尝试使用不完全类型的智能指针,就会得到编译时错误。不再有未定义的行为:如果您的代码编译,那么您在任何需要的地方都使用了完整的类型。class&nbsp;A{ &nbsp;&nbsp;&nbsp;&nbsp;class&nbsp;impl; &nbsp;&nbsp;&nbsp;&nbsp;std::unique_ptr<impl>&nbsp;ptr_;&nbsp;&nbsp;//&nbsp;ok!public: &nbsp;&nbsp;&nbsp;&nbsp;A(); &nbsp;&nbsp;&nbsp;&nbsp;~A(); &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;...};shared_ptr和unique_ptr在不同的地方需要完整的类型。原因不明,与动态删除和静态删除有关。确切的原因并不重要。事实上,在大多数代码中,确切地知道在哪里需要一个完整的类型并不是很重要。只要编写代码,如果你弄错了,编译器就会告诉你。但是,如果这对您有帮助,下面是一个表,其中记录了shared_ptr和unique_ptr关于完整性要求。如果成员需要一个完整的类型,那么条目有一个“C”,否则表条目将被填充“i”。Complete type requirements for unique_ptr and shared_ptr&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; unique_ptr&nbsp; &nbsp; &nbsp; &nbsp;shared_ptr+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; P()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; ||&nbsp; default constructor&nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; P(const P&)&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp;N/A&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; ||&nbsp; &nbsp; copy constructor&nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;P(P&&)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; ||&nbsp; &nbsp; move constructor&nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;~P()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; C&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; ||&nbsp; &nbsp; &nbsp; &nbsp;destructor&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;P(A*)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; C&nbsp; &nbsp; &nbsp; &nbsp; |+------------------------+---------------+---------------+|&nbsp; operator=(const P&)&nbsp; &nbsp;|&nbsp; &nbsp; &nbsp;N/A&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; ||&nbsp; &nbsp; copy assignment&nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+------------------------+---------------+---------------+|&nbsp; &nbsp; operator=(P&&)&nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; C&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; ||&nbsp; &nbsp; move assignment&nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; &nbsp; reset()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; C&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; I&nbsp; &nbsp; &nbsp; &nbsp; |+------------------------+---------------+---------------+|&nbsp; &nbsp; &nbsp; &nbsp;reset(A*)&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; C&nbsp; &nbsp; &nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; C&nbsp; &nbsp; &nbsp; &nbsp; |+------------------------+---------------+---------------+任何需要指针转换的操作都需要两种操作的完整类型。unique_ptr和shared_ptr.这个unique_ptr<A>{A*}构造函数可以避免不完整的构造函数。A只有当编译器不需要设置~unique_ptr<A>()..例如,如果您将unique_ptr在堆里,你可以用一个不完整的A..有关这一点的更多详细信息,请参阅白瑞·哈切特氏回答这里.

子衿沉夜

编译器需要定义Thing来生成MyClass的默认析构函数。如果显式声明析构函数并将其(空)实现移动到CPP文件,则代码应编译。
随时随地看视频慕课网APP
我要回答