C++ 模板指针类可以有虚拟析构函数吗?

C++ 模板指针类可以有虚拟析构函数吗?,c++,visual-c++,C++,Visual C++,在用自制的指针类实现pimpl习惯用法时,我遇到了一个令人惊讶的发现(我知道:为什么要使用自己的指针类?但请容忍我)。以下三个文件包含一个简单的示例: 指针.h: #pragma once template <typename T> class Pointer { public: Pointer(T*p=0) : _p(p) { } virtual ~Pointer() { delete _p; }

在用自制的指针类实现pimpl习惯用法时,我遇到了一个令人惊讶的发现(我知道:为什么要使用自己的指针类?但请容忍我)。以下三个文件包含一个简单的示例:

指针.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.cpp
的内部结构是什么样子。当我用MSVC 2008编译
main.cpp
时,我得到警告:

pointer.h(13) : warning C4150: deletion of pointer to incomplete type 'FooPrivate'; no destructor called
可以通过从析构函数中删除关键字virtual来避免该警告

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


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

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

没有
virtual
,只有一个地方将调用析构函数;在
~Foo
中,此时您可能已经完全定义了
FooPrivate
。如果在其他地方创建了
指针的另一个实例,您可能会得到警告,但由于您没有这样做,编译器可以告诉您行为安全


使用
virtual
,理论上可以从
指针
派生,并且新对象可以从
foopprivate
未完全定义的地方销毁。编译器不确定您不这样做,因此会发出警告。在这种简单的情况下,您可以放心地忽略它,但是如果您确实需要虚拟析构函数,那么最好牢记它。

因为您没有给出
FooPrivate
的完整定义,编译器不知道它的vtable是什么样子。因为它不能调用它找不到的虚拟函数,所以它将退出。

因为您为类
Foo
提供了析构函数,所以警告似乎完全不正确且虚假

为了检查我是否在文件[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;
}
#包括“foo.h”
#包括
使用名称空间std;
私有结构
{

FoopPrivate(){cout当你写bear with me的时候,我以为你自己使用智能指针的原因稍后会在你的帖子中出现。遗憾的是,事实并非如此。所以,我不得不问——为什么?你有没有研究过std::auto_ptr()或Boost智能指针()?在这里,
Foo.cpp
的内部结构非常重要……您是否真的有一个显式的
~Foo()
,或者您正在使用提供的编译器?您不必做任何事情,只要在编写之前在
Foo.cpp
中完全定义了
FooPrivate
,空的主体就可以了。@Dennis:
~Foo()
在类定义中声明。因此它是用户定义的(或者根本没有定义,在这种情况下,一段时间后可能会导致链接错误)“
Foo.cpp
的内容与
main.cpp
的编译无关,因为它们是不同的翻译单元,这是一个编译时间,而不是链接时间警告。@史蒂夫:我不是真的想发表那个评论,而是选择创建一个正确的答案。但是,理论上,用户定义的析构函数tor可能在定义
FooPrivate
之前就已经出现了,但是由于警告在没有
virtual
的情况下消失了,所以几乎肯定不是这样。你怎么知道在
Foo::~Foo
的站点上它是不完整的?只有当不完整类型的析构函数一旦完成,就证明是非平凡的。什么“你不是说第二段吗?请提供一个具体的例子。@阿尔夫:老实说,我在任何情况下都很难让VS2010发出关于这一点的警告,而且我没有访问VS2008的权限。重要的是,验证析构函数的多态性调用位置比验证析构函数的多态性调用位置要困难得多。”r可以静态调用。对于非虚拟析构函数,所有销毁都必须静态完成。一旦引入虚拟析构函数,编译器就会选择谨慎出错。我不确定,在任何情况下,这都可能是误报。让VS2010发出警告没有问题(我没有使用任何特殊选项),我仍然不理解你的第二段。我怀疑这段代码没有意义。一个具体的例子会很好。干杯,事实上它没有逃逸。它只是发出一个虚假的愚蠢警告。然后继续发出正确的代码…我现在意识到我应该发布Foo.cpp的详细信息。它看起来和你的完全一样。注意它编译时没有警告。发出警告的是main.cpp。
pointer.h(13) : warning C4150: deletion of pointer to incomplete type 'FooPrivate'; no destructor called
#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;
}