Kla*_*aim 236 c++ stl visual-studio-2010 unique-ptr c++11
我在头文件中有一些代码如下:
#include <memory>
class Thing;
class MyClass
{
std::unique_ptr< Thing > my_thing;
};
Run Code Online (Sandbox Code Playgroud)
如果我有一个CPP这个头不包含的Thing类型定义,那么这并不在VS2010 SP1的编译:
1> C:\ Program Files(x86)\ Microsoft Visual Studio 10.0\VC\include\memory(2067):错误C2027:使用未定义类型'Thing'
替换std::unique_ptr为std::shared_ptr和编译.
所以,我猜这是当前VS2010 std::unique_ptr的实现,需要完整的定义,而且完全依赖于实现.
或者是吗?它的标准要求中是否有某些东西使得std::unique_ptr实施只能使用前向声明?感觉很奇怪,因为它应该只有一个指针Thing,不应该吗?
How*_*ant 313
从这里采用.
C++标准库中的大多数模板都要求使用完整类型对它们进行实例化.但是shared_ptr并且unique_ptr是部分例外.某些但不是所有成员都可以使用不完整的类型进行实例化.这样做的动机是支持使用智能指针的pimpl等成语,并且不会冒未定义行为的风险.
当您有一个不完整的类型并调用delete它时,可能会发生未定义的行为:
class A;
A* a = ...;
delete a;
Run Code Online (Sandbox Code Playgroud)
以上是法律代码.它会编译.您的编译器可能会或可能不会发出上述代码的警告.当它执行时,可能会发生不好的事情.如果你很幸运,你的程序会崩溃.然而,一个更可能的结果是你的程序会默默地泄漏内存而~A()不会被调用.
使用auto_ptr<A>在上面的例子中并没有帮助.您仍然会获得与使用原始指针相同的未定义行为.
不过,在某些地方使用不完整的课程非常有用!这是在哪里shared_ptr和unique_ptr帮助.使用其中一个智能指针可以让你获得一个不完整的类型,除非必须有一个完整的类型.最重要的是,当需要具有完整类型时,如果您尝试在该点尝试使用不完整类型的智能指针,则会出现编译时错误.
没有更多未定义的行为:
如果您的代码编译,那么您已经在所需的任何地方使用了完整的类型.
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
Run Code Online (Sandbox Code Playgroud)
shared_ptr并unique_ptr要求在不同的地方使用完整的类型.原因是模糊的,与动态删除器和静态删除器有关.确切的原因并不重要.实际上,在大多数代码中,确切地知道完整类型的位置并不是很重要.只是代码,如果你弄错了,编译器会告诉你.
但是,如果它对您有帮助,这里有一个表格,其中记录了几个成员shared_ptr以及unique_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 |
+------------------------+---------------+---------------+
Run Code Online (Sandbox Code Playgroud)
任何需要指针转换的操作都需要完整的类型unique_ptr和shared_ptr.
该unique_ptr<A>{A*}构造可以用一个不完整的脱身A仅如果不需要编译器来建立一个呼叫~unique_ptr<A>().例如,如果你把它unique_ptr放在堆上,你可以逃脱不完整A.关于这一点的更多细节可以在BarryTheHatchet的答案中找到.
Igo*_*nko 41
编译器需要Thing的定义来生成MyClass的默认析构函数.如果显式声明析构函数并将其(空)实现移动到CPP文件,则应编译代码.
Pup*_*ppy 15
这不依赖于实现.它工作的原因是因为shared_ptr确定在运行时调用正确的析构函数 - 它不是类型签名的一部分.但是,unique_ptr析构函数是它的类型的一部分,它必须在编译时知道.
看起来当前的答案并没有确切地说明为什么默认构造函数(或析构函数)是问题但是在cpp中声明的空元素不是.
这是最新发生的事情:
如果外部类(即MyClass)没有构造函数或析构函数,则编译器会生成默认值.这个问题是编译器实际上在.hpp文件中插入了默认的空构造函数/析构函数.这意味着默认的contructor /析构函数的代码与主机可执行文件的二进制文件一起编译,而不是与库的二进制文件一起编译.但是,这个定义不能真正构建分部类.所以当链接器进入你的库的二进制文件并尝试获取构造函数/析构函数时,它找不到任何错误.如果构造函数/析构函数代码在.cpp中,那么您的库二进制文件可用于链接.
这与使用unique_ptr或shared_ptr无关,而其他答案似乎可能会混淆旧VC++中针对unique_ptr实现的错误(VC++ 2015在我的机器上工作正常).
故事的寓意是你的标题需要保持不受任何构造函数/析构函数的定义.它只能包含他们的声明.例如,~MyClass()=default;在hpp中将无法正常工作.如果允许编译器插入默认构造函数或析构函数,则会出现链接器错误.
另一方面注意:如果在cpp文件中有构造函数和析构函数后仍然出现此错误,那么很可能原因是您的库未正确编译.例如,有一次我只是在VC++中将项目类型从Console改为库,我得到了这个错误,因为VC++没有添加_LIB预处理器符号,并且产生完全相同的错误消息.
只是为了完整性:
题主:啊
class B; // forward declaration
class A
{
std::unique_ptr<B> ptr_; // ok!
public:
A();
~A();
// ...
};
Run Code Online (Sandbox Code Playgroud)
来源 A.cpp:
class B { ... }; // class definition
A::A() { ... }
A::~A() { ... }
Run Code Online (Sandbox Code Playgroud)
类 B 的定义必须被构造函数、析构函数和任何可能隐式删除 B 的东西看到。(虽然构造函数没有出现在上面的列表中,但在 VS2017 中,甚至构造函数也需要 B 的定义。考虑到这一点,这是有道理的如果构造函数中出现异常,则 unique_ptr 将再次销毁。)
| 归档时间: |
|
| 查看次数: |
40791 次 |
| 最近记录: |