模板化的Pointer类可以有一个虚拟析构函数吗?

时间:2011-06-28 17:02:52

标签: c++ visual-c++

当用自制指针类实现pimpl成语时,我遇到了一个令人惊讶的启示(我知道:为什么要自己滚动?但请耐心等待)。以下三个文件包含一个最小示例:

Pointer.h:

#pragma once 

template <typename T>
class Pointer
{
public:
    Pointer(T*p=0)
        : _p(p)
    {
    }
    virtual ~Pointer()
    {
        delete _p;
    }
private:
    void operator=(const Pointer&);
    Pointer(const Pointer&);

private:
    T*_p;
};

foo.h中:

#pragma once
#include "Pointer.h"

struct Foo
{
    Foo();
    ~Foo();

private:
    void operator=(const Foo&);
    Foo(const Foo&);

private:
    Pointer<struct FooPrivate> p;
};

main.cpp中:

#include "Foo.h"

int main(int argc, char* argv[])
{
    Foo foo;
    return 0;
}

不要介意Foo.cpp的内部结构。当我使用MSVC 2008编译main.cpp时,我收到警告:

pointer.h(13) : warning C4150: deletion of pointer to incomplete type 'FooPrivate'; no destructor called

可以通过从Pointers析构函数中删除关键字virtual来避免警告。

这对我没有意义。这个警告是合法的,还是MSVC编译器中的错误?如果是这样,我可以安全地忽略警告吗?

我知道在这种情况下使析构函数虚拟是没有意义的,但请记住,这只是一个最小的可编译示例。我原来的代码要复杂得多。

4 个答案:

答案 0 :(得分:4)

没有virtual,析构函数只会被调用一个地方;在~Foo范围内,此时您可能已完全定义FooPrivate。如果在其他地方创建了另一个Pointer<FooPrivate>实例,您可能会收到警告,但由于您没有,编译器可以告诉您安全行为。

使用virtual,理论上可以从Pointer<FooPrivate>派生,并且可以从FooPrivate未完全定义的某个地方销毁新对象。编译器不肯定你不这样做,所以它会发出警告。在这个简单的案例中你可以放心地忽略它,但如果你真的需要一个虚拟的析构函数,那么把它放在心上可能是个好主意。

答案 1 :(得分:2)

由于您要为班级Foo提供析构函数,因此警告似乎完全不正确&amp;杂散。

只是检查我在文件[foo.cpp]中添加了这段代码:

#include "foo.h"
#include <iostream>
using namespace std;

struct FooPrivate
{
    FooPrivate() { cout << "FooPrivate::<init>" << endl; }
    ~FooPrivate() { cout << "FooPrivate::<destroy>" << endl; }
};

Foo::Foo()
    : p( new FooPrivate )
{
    cout << "Foo::<init>" << endl;
}

Foo::~Foo()
{
    cout << "Foo::<destroy>" << endl;
}

它产生了与您相同的警告(使用Visual C ++ 10.0),但输出

  

FooPrivate ::&LT; INIT&GT;
  FOO ::&LT; INIT&GT;
  FOO ::&LT;破坏&GT;
  FooPrivate ::&LT;破坏&GT;

显然,可执行文件没有做傻警告说的那样......

干杯&amp;第h。,

答案 2 :(得分:0)

对不完整类型调用delete是未定义行为。

答案 3 :(得分:0)

因为您没有给出FooPrivate的完整定义,所以编译器不知道它的vtable是什么样的。由于无法调用无法定位的虚函数,因此它会失败。