C++ 为什么';t std::shared#ptr需要知道完整类型,如果它是';s是由非null构造的?
我在factory.h中有一个工厂函数,它将C++ 为什么';t std::shared#ptr需要知道完整类型,如果它是';s是由非null构造的?,c++,c++11,C++,C++11,我在factory.h中有一个工厂函数,它将std::shared_ptr返回到foo.h中的基类factory.h使用对基类的前向声明,而不是包含foo.h。如下代码所示: 工厂.h: #include <memory> // forward declaration class foo; std::shared_ptr<foo> create_foo_A(int A); std::shared_ptr<foo> create_foo_B(int A, i
std::shared_ptr
返回到foo.h中的基类factory.h使用对基类的前向声明,而不是包含foo.h。如下代码所示:
工厂.h:
#include <memory>
// forward declaration
class foo;
std::shared_ptr<foo> create_foo_A(int A);
std::shared_ptr<foo> create_foo_B(int A, int B);
void work_with_foo(std::shared_ptr<foo> ptr);
#include "factory.h"
int main()
{
int type = 1;
std::shared_ptr<foo> ptr(nullptr); // <--- compile warning
if (type == 1)
ptr = create_foo_A(5566);
else
ptr = create_foo_B(5566, 7788);
work_with_foo(ptr);
return 0;
}
#include "factory.h"
int main()
{
int type = 1;
if (type == 1)
{
std::shared_ptr<foo> ptr = create_foo_A(5566); // <--- OK
work_with_foo(ptr);
}
else
{
std::shared_ptr<foo> ptr = create_foo_B(5566, 7788); // <--- OK
work_with_foo(ptr);
}
return 0;
}
警告信息是:
warning C4150 : deletion of pointer to incomplete type 'foo'; no destructor called
这是合理的,因为std::shared_ptr
不知道foo的完整类型。如果main.cpp包含foo.h,则可以删除此警告
但是如果std::shared_ptr
是用非nullptr
初始化的,编译器不会发出警告
main.cpp:
#include <memory>
// forward declaration
class foo;
std::shared_ptr<foo> create_foo_A(int A);
std::shared_ptr<foo> create_foo_B(int A, int B);
void work_with_foo(std::shared_ptr<foo> ptr);
#include "factory.h"
int main()
{
int type = 1;
std::shared_ptr<foo> ptr(nullptr); // <--- compile warning
if (type == 1)
ptr = create_foo_A(5566);
else
ptr = create_foo_B(5566, 7788);
work_with_foo(ptr);
return 0;
}
#include "factory.h"
int main()
{
int type = 1;
if (type == 1)
{
std::shared_ptr<foo> ptr = create_foo_A(5566); // <--- OK
work_with_foo(ptr);
}
else
{
std::shared_ptr<foo> ptr = create_foo_B(5566, 7788); // <--- OK
work_with_foo(ptr);
}
return 0;
}
#包括“factory.h”
int main()
{
int型=1;
如果(类型==1)
{
std::shared_ptr ptr=create_foo_A(5566);//恐怕你的问题完全是反方向的。:-)
在第二个示例中,调用create_foo_A()和create_foo_B(),您实际上是在向编译器承诺,这些函数在链接到程序时,将返回指向从foo派生的类实例的指针。编译器不需要知道或关心foo的组成部分;此时,由这些函数决定什么是真正的foo
但在您的第一个示例中,行:
std::shared_ptr<foo> ptr(nullptr);
std::shared_ptr ptr(nullptr);
构造指向foo的共享指针,程序希望为要调用的共享指针找到析构函数。由于您尚未定义foo,只声明它是一个类,编译器不知道该类的外观(它是一个不完整的类型)并抱怨没有那个析构函数。所以std::shared\u ptr
是三件事,都包在一个整洁的小包裹里
首先,它是一个指向某些数据的指针,通过重写可以访问这些数据,这使得使用它与使用原始C指针非常相似
第二部分是参考计数器。它跟踪std::atomic
的等价物,即所有std::shared_ptr
在进入/离开范围时复制第一个增量/减量
第三部分是销毁函数——更确切地说,是指向所述销毁函数的指针
从另一个复制std::shared_ptr
时,它使用引用计数器/销毁函数/指向源std::shared_ptr
数据的指针。因此,它不需要编写销毁函数,也不需要知道销毁函数的功能
当您在不复制的情况下创建std::shared_ptr
时,它必须创建销毁函数。默认销毁函数是调用Foo
的析构函数的函数:它需要Foo
的完整声明
std::shared_ptr
将调用销毁函数,即使指针是nullptr
,因此即使在空的情况下,它也需要写入销毁函数
因此,简短的故事是:无论您在何处创建一个std::shared_ptr
,而不是通过复制或移动另一个std::shared_ptr
,您必须提供手动销毁功能,或者必须使Foo::~Foo
可见(即Foo
的完整声明)
调用返回std::shared_ptr
的函数的情况是从函数体内部复制它们的:在该体中,无论那些std::shared_ptr
最终来自何处,满足上述两个要求之一
在这种情况下,您只需在nullptr
上创建它,它就必须编写并存储销毁函数,这需要完全访问该类
如果您确实需要std::shared_ptr ptr(nullptr);
而不访问foo
,请尝试std::shared_ptr ptr(nullptr,[](foo*){});
,它应该传递一个不做任何事情的销毁函数。
std::shared_ptr<foo> ptr(nullptr);
并且这两个被指定为等效的:
constexpr shared_ptr(nullptr_t) : shared_ptr() { }
在这个场景中,为什么std::shared_ptr不需要知道类foo的完整类型
create\u foo\u A
和create\u foo\u B
的定义需要知道foo
的完整类型,但声明不需要
简言之,shared\U ptr::shared\U ptr(U*)
构造函数需要知道U
的完整定义。但除此之外,几乎没有人知道。在这个答案中有一个更完整的调查:
所以如果你说:
std::shared_ptr<foo> ptr((foo*)0);
但是shared\u ptr
已经知道了完整的类型-毕竟,它必须知道它才能在第一个位置存在该类型的对象。销毁代码绑定在创建第一个shared\u ptr
的位置-在create\u foo\u X
函数中。@Xeo:我想这是一个答案,不是注释!谢谢!我想我错过了这个链接。如果您将共享的\u ptr
构造为空或带有空ptr
,是否可以生成一个不应调用删除器的标准引用?