为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C++
繁星coding 2019-07-01 15:51:30
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不是吗?
查看完整描述

3 回答

?
梦里花落0921

TA贡献1772条经验 获得超6个赞

采自这里.

C+标准库中的大多数模板都要求用完整类型实例化它们。不过shared_ptrunique_ptr部分例外。一些成员(但不是所有成员)可以用不完整类型实例化。这样做的动机是支持这样的成语,例如PIMPL使用智能指针,并且不冒未定义行为的风险。

当您有一个不完整的类型并且您调用时,可能会发生未定义的行为。delete在上面:

class A;A* a = ...;delete a;

以上是法律法规。它将汇编。您的编译器可能对上述代码发出类似上述代码的警告,也可能不会发出警告。当它执行时,坏的事情可能会发生。如果你很幸运你的程序会崩溃。然而,更有可能的结果是,您的程序将悄悄地泄漏内存,作为~A()不会被打电话的。

使用auto_ptr<A>在上面的例子中没有帮助。您仍然会得到相同的未定义行为,就像使用了原始指针一样。

然而,在某些地方使用不完整的类是非常有用的!这里是shared_ptrunique_ptr帮助。使用这些智能指针之一将使您可以避免使用不完整类型,除非需要使用完整类型。最重要的是,当需要有一个完整的类型时,如果尝试使用不完全类型的智能指针,就会得到编译时错误。

不再有未定义的行为:

如果您的代码编译,那么您在任何需要的地方都使用了完整的类型。

class A{
    class impl;
    std::unique_ptr<impl> ptr_;  // ok!public:
    A();
    ~A();
    // ...};

shared_ptrunique_ptr在不同的地方需要完整的类型。原因不明,与动态删除和静态删除有关。确切的原因并不重要。事实上,在大多数代码中,确切地知道在哪里需要一个完整的类型并不是很重要。只要编写代码,如果你弄错了,编译器就会告诉你。

但是,如果这对您有帮助,下面是一个表,其中记录了shared_ptrunique_ptr关于完整性要求。如果成员需要一个完整的类型,那么条目有一个“C”,否则表条目将被填充“i”。

Complete type requirements for unique_ptr and shared_ptr


                            unique_ptr       shared_ptr

+------------------------+---------------+---------------+

|          P()           |      I        |      I        |

|  default constructor   |               |               |

+------------------------+---------------+---------------+

|      P(const P&)       |     N/A       |      I        |

|    copy constructor    |               |               |

+------------------------+---------------+---------------+

|         P(P&&)         |      I        |      I        |

|    move constructor    |               |               |

+------------------------+---------------+---------------+

|         ~P()           |      C        |      I        |

|       destructor       |               |               |

+------------------------+---------------+---------------+

|         P(A*)          |      I        |      C        |

+------------------------+---------------+---------------+

|  operator=(const P&)   |     N/A       |      I        |

|    copy assignment     |               |               |

+------------------------+---------------+---------------+

|    operator=(P&&)      |      C        |      I        |

|    move assignment     |               |               |

+------------------------+---------------+---------------+

|        reset()         |      C        |      I        |

+------------------------+---------------+---------------+

|       reset(A*)        |      C        |      C        |

+------------------------+---------------+---------------+

任何需要指针转换的操作都需要两种操作的完整类型。unique_ptrshared_ptr.

这个unique_ptr<A>{A*}构造函数可以避免不完整的构造函数。A只有当编译器不需要设置~unique_ptr<A>()..例如,如果您将unique_ptr在堆里,你可以用一个不完整的A..有关这一点的更多详细信息,请参阅白瑞·哈切特氏回答这里.


查看完整回答
反对 回复 2019-07-01
?
子衿沉夜

TA贡献1828条经验 获得超3个赞

编译器需要定义Thing来生成MyClass的默认析构函数。如果显式声明析构函数并将其(空)实现移动到CPP文件,则代码应编译。


查看完整回答
反对 回复 2019-07-01
  • 3 回答
  • 0 关注
  • 979 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信