将我的 gcc 编译器更新到 gcc12.x 版本时,我遇到了一些新警告。
考虑这段代码:
#include <memory>
struct Test
{
inline static int i = 0;
Test() { ++i; }
~Test() { --i; }
};
struct MHeap
{
template<typename T>
void manage()
{
using Td = std::remove_extent_t<T>;
std::shared_ptr<T> ptr(new T(), std::default_delete<Td[]>{});
}
};
int main()
{
MHeap mem;
mem.manage<Test[3]>();
}
$ g++ -O2 -Wall main.cpp
错误:
In member function 'void MHeap::manage() [with T = Test [3]]',
inlined from 'int main()' at <source>:24:25:
<source>:17:32: warning: pointer used after 'void operator delete [](void*, std::size_t)' [-Wuse-after-free]
17 | std::shared_ptr<T> ptr(new T(), std::default_delete<Td[]>{});
| ^~~~~~~
In file included from /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/memory:75,
from <source>:2:
In member function 'typename std::enable_if<std::is_convertible<_Up (*)[], _Tp (*)[]>::value>::type std::default_delete<_Tp []>::operator()(_Up*) const [with _Up = Test; _Tp = Test]',
inlined from 'std::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter, _Alloc) [with _Ptr = Test*; _Deleter = std::default_delete<Test []>; _Alloc = std::allocator<void>; <template-parameter-2-4> = void; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:958:11,
inlined from 'std::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter) [with _Ptr = Test*; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:939:57,
inlined from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*, _Deleter) [with _Yp = Test; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; _Tp = Test [3]; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:1478:17,
inlined from 'std::shared_ptr<_Tp>::shared_ptr(_Yp*, _Deleter) [with _Yp = Test; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; _Tp = Test [3]]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr.h:232:48,
inlined from 'void MHeap::manage() [with T = Test [3]]' at <source>:17:28,
inlined from 'int main()' at <source>:24:25:
/opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/unique_ptr.h:132:11: note: call to 'void operator delete [](void*, std::size_t)' here
132 | delete [] __ptr;
| ^~~~~~~~~~~~~~~
此警告有效吗?
编译器:GCC 12
有趣的是,使用大小小于 3 的数组即
mem.manage<Test[2]>()
会清除警告。
new T
创建一个指向 new
的单个实例的 T
d 指针(在本例中为 T=Test[3]
)。因此,您必须使用 delete
而不是 delete[]
来释放该实例。但是,由于您正在构造一个在 shared_ptr<Test[]>
'd 指针而不是 delete[]
'd 指针上调用 new
的 new[]
,因此您正在调用 未定义的行为。
为数组构造shared_ptr
的
正确方法是使用:
std::shared_ptr<T[]> ptr(new T[size]);
// or:
auto ptr = std::make_shared<T[]>(size);
对于你的例子来说,那就是:
using Td = std::remove_extent_t<T>;
std::shared_ptr<Td[]> ptr(new Td[std::extent_v<T>]);
// or:
auto ptr = std::make_shared<Td[]>(std::extent_v<T>);
这将正确地
new[]
数组和delete[]
它。