模板化的指针类是否可以具有虚拟析构函数
Can a templated Pointer class have a virtual destructor?
当我用自制的指针类实现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是什么样子的。由于它不能调用它找不到的虚拟函数,所以它放弃了。
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 是否可以使用函数指针调用虚拟析构函数?
- 在没有动态内存的世界中,我是否需要虚拟析构函数?
- 程序永远不会进入虚拟析构函数
- C++ std::vector 中的虚拟析构函数继承
- 哪种方法更适合处理虚拟析构函数?
- 拥有"受保护的非虚拟析构函数"与"受保护虚拟析构构函数"有什么好处
- 带有未解析外部元素的C++虚拟析构函数
- 即使基类和派生类只使用基元数据类型,我是否需要定义虚拟析构函数
- C++切片和虚拟析构函数
- C++虚拟继承、虚拟析构函数和 dynamic_cast<void*>
- 添加虚拟析构函数会使代码大小膨胀
- 应该是虚拟析构函数吗?但是怎么做呢?
- 虚拟析构函数将对象移出 rodata 部分
- 如何将 std::unique_ptr<Parent> 与具有受保护虚拟析构函数的只读父类一起使用
- DIRECTX9 中自定义顶点的虚拟析构函数
- 为什么缺少虚拟析构函数不会导致内存泄漏?
- std::unique_ptr 在虚拟析构函数上重置 SIGABRT
- C++11 中默认纯虚拟析构函数的正确放置
- 在派生类中重写哪个基类的虚拟析构函数