模板化的指针类是否可以具有虚拟析构函数

Can a templated Pointer class have a virtual destructor?

本文关键字:虚拟 析构函数 是否 指针      更新时间:2023-10-16

当我用自制的指针类实现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.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编译器中的错误?如果是,我可以放心地忽略警告吗?

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

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

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

由于您为类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;
}

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

FooPrivate::<init>
Foo::<init>
Foo::<销毁>
FooPrivate::<销毁>

很明显,可执行文件并没有像愚蠢的警告所说的那样…

干杯&hth。,

在不完全类型上调用delete是Undefined Behavior。

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