编译器未识别重载delete运算符引发LNK2019链接错误求助
先还原下你的场景:参考Pavel Yosifovich的文章开发Windows内核驱动,重载了带池类型和标签的operator new,以及单参数operator delete,但在删除Vector<ULONG>对象时,两种调用方式都触发了LNK2019链接错误,最后把operator delete改成双参数签名才解决。下面逐个解答你的三个疑问:
疑问1:场景1中链接器为何在Vector类中查找delete运算符?
当你执行delete vector时,编译器会为Vector<ULONG>类生成一个标量删除析构函数(scalar deleting destructor)——这是编译器自动生成的辅助函数,职责是先调用对象的析构函数,再释放内存。
关键在于:你创建对象用的是带额外参数的placement new:new(NonPagedPool, 'DaOr') Vector<ULONG>()。根据C++规则,当使用带额外参数的placement new分配对象时,编译器会自动关联对应的placement delete(参数列表要和new的额外参数完全匹配)。这个关联逻辑会被嵌入到编译器生成的标量删除析构函数中,所以它会尝试调用双参数的operator delete,而不是你手写的单参数版本。链接器找不到这个双参数重载,就会报错说“未解析外部符号”,且错误来源指向Vector的标量删除析构函数。
疑问2:场景2中悬浮提示与链接错误的运算符签名为何不一致?
这是IDE静态分析和编译器实际生成代码的行为差异:
- 悬浮提示是VS的静态代码分析,它只看你写的
delete (void*)vector字面代码,匹配到了你显式声明的单参数operator delete(void*),所以提示会调用这个重载。 - 但编译器在处理内核模式代码时,会保留placement new的上下文关联——哪怕你把
Vector<ULONG>*强制转成void*,编译器仍然知道这个指针是通过带双参数的placement new分配的,所以生成的代码还是会尝试调用对应的双参数operator delete。链接器处理的是编译器生成的目标文件,自然会报错找不到双参数的重载,和IDE的静态提示就出现了不一致。
疑问3:两种场景均仅传入一个参数,为何编译器识别为双参数delete?
核心原因是placement new和placement delete的成对匹配规则:
你用带额外参数(POOL_TYPE和ULONG标签)的placement new创建对象时,编译器会把这个分配操作和对应的placement delete(参数为void* + new的额外参数)绑定在一起。不管你delete的时候写的是delete vector还是delete (void*)vector,编译器都会根据new时的上下文,自动生成调用双参数delete的代码——哪怕你没有手动传递额外参数,这些参数的信息已经在编译阶段被关联到了对象的分配逻辑中。
简单说,new的重载和delete的重载是成对绑定的,你用了多参数的new,编译器就会找对应的多参数delete,和你delete时写的参数数量无关。
内容的提问来源于stack exchange,提问作者Oriel Cochavi

