shared_ptr析构函数,复制和不完整类型

时间:2014-08-20 12:47:10

标签: c++ c++11 shared-ptr incomplete-type

我有一个像这样的头文件foo.h(省略了无关的东西):

#pragma once
#include <memory>

class Bar;


struct Foo
{
  std::shared_ptr<Bar> getBar();

  std::shared_ptr<const Bar> getBar() const
  {
    return const_cast<Foo*>(this)->getBar();
  }
};

getBar()的非常量重载是在.cpp文件中实现的,该文件也可以看到Bar的完整定义。

foo.h包含在另一个文件中时(看不到Bar的定义),VS 2010会给我一个警告:

warning C4150: deletion of pointer to incomplete type 'Bar'; no destructor called

getBar()的const重载上(或者实际上在从该重载实例化的标准库中的深处)。

我的问题是这个警告是否可以安全地被忽略。

我看待它的方式,在std::shared_ptr<Bar>中调用了getBar() const的两个成员函数:转换构造函数和析构函数。

// converting constructor
template <class Y>
std::shared_ptr<const Bar>::shared_ptr(std::shared_ptr<Y> &&r)

这用于从getBar() const的返回值初始化getBar()的返回值。这并未列出任何先决条件(C ++ 1127.2.2.1§20-22),这些先决条件需要Y(在我的情况下为Bar)才能完成。

// destructor
std::shared_ptr<const Bar>::~shared_ptr()

27.2.2.2§1规定当被销毁的共享指针为空时,没有副作用。

我理解为什么我会收到警告 - 析构函数代码也必须关心必须在存储指针上调用delete时的情况,并且此代码确实会删除不完整的类型。但是我看到它的方式,在我的情况下永远无法达到,所以getBar() const是安全的。

我是否正确,或者我是否忽略了某个电话或可能使getBar() const实际删除不完整类型的内容?

2 个答案:

答案 0 :(得分:7)

我找不到警告的理由。我也不能用clang / libc ++复制警告。

一般情况下,如果shared_ptr<Bar>没有看到shared_ptr<Bar>的构造需要Bar*,并且可选择删除,则无法确定{{1}是永远被调用。无法知道~Bar()中存储了哪些删除器,并且在shared_ptr<Bar>旁边的d中存储了一些未知的删除器shared_ptr<Bar>(比如说​​Bar* p),不要求d(p)致电~Bar()

例如,您的Bar可能没有可访问的析构函数:

class Bar
{
    ~Bar();
};

您的Foo::getBar()可以像这样实施:

std::shared_ptr<Bar>
Foo::getBar()
{
    // purposefully leak the Bar because you can't call ~Bar()
    return std::shared_ptr<Bar>(new Bar, [](Bar*){});
}

如果没有看到foo.cpp,编译器就无法知道。

此警告对我来说似乎是一个编译器错误,或者可能是std::shared_ptr实现中的错误。

你能忽略这个警告吗?我不知道。在我看来,你正在处理实现中的错误,因此bug很可能意味着警告是真实的。但假设完全符合实现,我认为Bar不需要在您显示的代码中成为完整类型。

答案 1 :(得分:2)

没有;不能安全地忽略警告。您的代码创建了一个shared_ptr对象。 shared_ptr构造函数是一个创建和存储删除器的模板。通过添加代码来在标题中创建shared_ptr,您就会过早地实例化模板构造函数。

shared_ptr使用的删除技巧允许您在定义类之前声明它们,但在首次使用它们之前,它们仍然需要查看完整类型。您的代码无法保证永远调用Bar的析构函数。更糟糕的是,今天,它甚至可以工作,但可能是定时炸弹,使你的代码中出现一个非常难以调试的错误。

问题与代码中指针的内容无关。只是事实上你有代码创建一个共享指针,它没有看到Bar的完整定义就足够了。

修复很简单。在完成Bar的声明之后,只需将该代码放入实现文件中。

编辑:g ++给出的警告符合这种情况,但代码却没有。现在我要留下这个答案,直到我有时间去调查(或者其他人弄清楚为什么g ++会发出这个警告)。