C++ 根据新放置跟踪放置删除
我正在开发一个类似内存泄漏检测器的工具。我可以跟踪新位置,但如何跟踪位置删除。我做了很多研发,发现placement delete不能直接调用,它是在异常时由构造函数调用的。那么,如何根据新的放置跟踪放置删除C++ 根据新放置跟踪放置删除,c++,C++,我正在开发一个类似内存泄漏检测器的工具。我可以跟踪新位置,但如何跟踪位置删除。我做了很多研发,发现placement delete不能直接调用,它是在异常时由构造函数调用的。那么,如何根据新的放置跟踪放置删除 如果您有任何帮助,我们将不胜感激……如果您没有,我们将无需跟踪 Placement new意味着将对象放置在内存位置。已为其分配了存储空间。您不会对对象本身调用任何形式的delete,因为这是从何处获取存储的工作 也就是说,在这方面: void* memory = operator new
如果您有任何帮助,我们将不胜感激……如果您没有,我们将无需跟踪 Placement new意味着将对象放置在内存位置。已为其分配了存储空间。您不会对对象本身调用任何形式的delete,因为这是从何处获取存储的工作 也就是说,在这方面:
void* memory = operator new(sizeof(int)); // A
new (memory) int; // B
operator delete(memory); // C
A和C是您应该跟踪的对象,而不是B。1)对新的allocs使用placement new
2) 使用一个包装函数(例如)
delete\u对象
,它通过分配器调用,还有一个用于delete\u数组
。请注意,指针的对齐可能会偏离您返回的实际分配(对于数组)。在基类接口中声明运算符new/new[]/delete/delete[]
,(以及内置变量,或隐藏它们)。然后实现您需要的功能
如果您只想跟踪特定类层次结构(或一组对象)中对象的泄漏,那么这在某些情况下应该是可用的
插图(请注意,伪代码如下所示):
/*选项1)对所有类实例使用指定的分配器*/
命名空间{const char*const TypeID(“MON::t_object”);}
void*MON::t_对象::运算符新建(大小\u t大小){
void*常量分配(AllocatorForType(TypeID).operator_new(size));
std::cout您希望将分配和解除分配配对:
- malloc/免费
- 新增/删除(“常规”表格)
- 新建[]/删除[]
但是,什么是placement new?(显式:取一个void*并通常简称为“placement new”,而不是其他的placement形式的new。)它不是delete,而是显式析构函数调用
T*p=new(mem)T();
/p->~T()
Placement new实际上并没有分配任何东西,它只是调用构造函数的语法糖。你不需要也不应该跟踪它。它甚至比其他形式更奇怪,因为首先调用“destroy”位,然后用另一个替换被破坏的对象(与其他形式的顺序相反):
首先,不是针对OP而是针对其他读者:据我所知,OP并不是在预分配存储中构造对象
我不是在说这个
操作:不需要,只需将operator delete
的放置表单转发到普通的operator delete
。毕竟,对于任何成功构建的动态分配对象,都将调用释放函数。因此,无论发生什么情况,都需要支持它
如果您想将调试信息与分配的内存关联起来,以便在释放功能中使用,那么一个实用的选项是分配比请求多一点的内存,并将信息放在该块的开始或结束处,将指针返回到未使用的部分。然后运算符delete
需要执行相反的操作。注意:对齐
我想一个不切实际的选择是使用静态std::map
(或其他关联数组,如哈希表)。它运行在线程安全问题等方面,但它避免了对齐问题
附录,一个完整的示例:
// Note: while this example works nicely, it doesn't support threading.
#include <iostream>
#include <new> // std::bad_alloc
#include <stddef.h> // ptrdiff_t, size_t, max_align_t
#include <stdlib.h> // malloc, EXIT_*,
typedef unsigned char Byte;
struct Source_reference
{
char const* filename;
int line_number;
};
#define SOURCE_REF Source_reference{ __FILE__, __LINE__ }
auto operator<<( std::ostream& stream, Source_reference const& ref )
-> std::ostream&
{
if( ref.filename == nullptr )
{
return stream << "[unknown source location]";
}
return stream << "\"" << ref.filename << "\"@" << ref.line_number;
}
struct Block_info
{
Source_reference source_ref;
Block_info* p_prev;
Block_info* p_next;
void link_in_after( Block_info& predecessor )
{
p_prev = &predecessor;
p_next = predecessor.p_next;
predecessor.p_next = this;
p_next->p_prev = this;
}
void unlink()
{
p_next->p_prev = p_prev;
p_prev->p_next = p_next;
}
};
namespace g {
size_t const max_align = sizeof( max_align_t );
size_t const prefix_size =
((sizeof( Block_info ) + max_align - 1)/max_align)*max_align;
Block_info block_list_header =
{{nullptr,0}, &block_list_header, &block_list_header};
} // namespace g
auto tracking_alloc( size_t const n_bytes_requested )
-> void*
{
size_t const n_bytes = n_bytes_requested + g::prefix_size;
Byte* const result = static_cast<Byte*>( malloc( n_bytes ) );
if( !result ) { throw std::bad_alloc(); }
Block_info* const p_info = ::new( result ) Block_info();
p_info->link_in_after( g::block_list_header );
return result + g::prefix_size;
}
void tracking_dealloc( void* p )
{
Block_info* p_info = reinterpret_cast<Block_info*>(
static_cast<Byte*>( p ) - g::prefix_size
);
p_info->unlink();
free( p_info );
}
auto operator new( size_t const n_bytes )
-> void*
{ return tracking_alloc( n_bytes ); }
auto operator new[]( size_t const n_bytes )
-> void*
{ return operator new( n_bytes ); }
void operator delete( void* p )
{ tracking_dealloc( p ); }
void operator delete[]( void* p )
{ operator delete( p ); }
auto operator new( size_t const n_bytes, Source_reference const& ref )
-> void*
{
Byte* const p = static_cast<Byte*>( operator new( n_bytes ) );
Block_info* const p_info = reinterpret_cast<Block_info*>( p - g::prefix_size );
p_info->source_ref = ref;
return p;
}
void operator delete( void* p, Source_reference const& )
{
using namespace std;
cout << "!placement delete called." << endl;
operator delete( p );
}
void list_blocks()
{
using namespace std;
cout << "Known allocated blocks:" << endl;
for(
Block_info* p_info = g::block_list_header.p_next;
p_info != &g::block_list_header;
p_info = p_info->p_next
)
{
void* const p_data = reinterpret_cast<Byte*>( p_info ) + g::prefix_size;
cout
<< "- Basic allocation " << p_data
<< " from " << p_info->source_ref << "."
<< endl;
}
cout << "- End list." << endl;
}
#include <vector>
auto main()
-> int
{
using namespace std;
int* p = new( SOURCE_REF ) int( 42 );
cout << "An int allocated with ref at " << p << "." << endl;
list_blocks();
int* p2 = new int( 43 );
cout << "\nAn int allocated sans ref at " << p << "." << endl;
list_blocks();
{
vector<double> v( 3 );
cout << "\nA vector constructed" << endl;
list_blocks();
try
{
struct Ungood{ Ungood() { throw 666; } };
cout << "\nAllocating with ref an object that fails construction." << endl;
new( SOURCE_REF ) Ungood;
}
catch( ... )
{}
list_blocks();
delete p;
cout << "\nThe int-with-ref deleted." << endl;
list_blocks();
}
cout << "\nThe vector destroyed" << endl;
list_blocks();
delete p2;
cout << "\nThe int-sans-ref deleted." << endl;
list_blocks();
}
//注意:虽然这个示例运行良好,但它不支持线程。
#包括
#包括//标准::错误的分配
#包括//ptrdiff\u t、size\u t、max\u align\t
#包括//malloc,退出*,
typedef无符号字符字节;
结构源引用
{
字符常量*文件名;
内线号;
};
#定义源引用源引用{{uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu}
自动操作员无效*
{
size\t const n\u bytes=n\u bytes\u requested+g::prefix\u size;
Byte*const result=static_cast(malloc(n_字节));
如果(!result){throw std::bad_alloc();}
Block_info*const p_info=::新建(结果)Block_info();
p_info->link_in_after(g::block_list_header);
返回结果+g::前缀大小;
}
无效跟踪\u解除锁定(无效*p)
{
块信息*p信息=重新解释(
静态类型转换(p)-g::前缀大小
);
p_info->取消链接();
免费(p_信息);
}
自动运算符新建(大小常量n字节)
->空虚*
{return tracking_alloc(n_字节);}
自动运算符新[](大小常量n字节)
->空虚*
{返回运算符new(n_字节);}
void运算符删除(void*p)
{tracking_dealoc(p);}
void运算符delete[](void*p)
{运算符delete(p);}
自动运算符新建(大小常量n字节,源引用常量&ref)
->空虚*
{
字节*常量p=静态_转换(运算符新(n_字节));
块信息*常量p\u信息=重新解释转换(p-g::前缀大小);
p\u info->source\u ref=ref;
返回p;
}
void操作符delete(void*p,Source\u reference const&)
{
使用名称空间std;
我对你的一个例子有点意见:
MyClass(char * c)
{
a = new char[10];
strcpy(a, c);
throw here ...
}
~MyClass()
{
delete[] a;
}
我认为您的问题在于在构造函数中使用new
,而不将其包装到某种资源管理器中。如果构造函数抛出,则无论对象在其中如何更新(new或placement new),分配给它的内存都将泄漏,除非它由另一个对象管理
struct Janitor {
Janitor(char* item) : mgr(item) {}
~Janitor() { if uncaught_exception() delete [] mgr; }
char *mgr;
};
MyClass(char * c)
{
Janitor j(new char[10]);
// j is destroyed both if the rest of the contstructor succeeds
// and if it throws
//unsafe code
strcpy(j.mgr, c);
...
//at the end
//pass the resource management to MyClass
a = j.mgr;
};
其中一些可能也有帮助。
还不完全清楚你想做什么。你说的“跟踪”是什么意思?那么您的意思是,当使用placementnew
构造对象时,您希望跟踪这一事实,并确保在回收内存之前显式调用其析构函数?+1对于知识更丰富的提问者
An int allocated with ref at 0x213c0.
Known allocated blocks:
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.
An int allocated sans ref at 0x213c0.
Known allocated blocks:
- Basic allocation 0x21410 from [unknown source location].
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.
A vector constructed
Known allocated blocks:
- Basic allocation 0x21460 from [unknown source location].
- Basic allocation 0x21410 from [unknown source location].
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.
Allocating with ref an object that fails construction.
!placement delete called.
Known allocated blocks:
- Basic allocation 0x21460 from [unknown source location].
- Basic allocation 0x21410 from [unknown source location].
- Basic allocation 0x213c0 from "foo.cpp"@134.
- End list.
The int-with-ref deleted.
Known allocated blocks:
- Basic allocation 0x21460 from [unknown source location].
- Basic allocation 0x21410 from [unknown source location].
- End list.
The vector destroyed
Known allocated blocks:
- Basic allocation 0x21410 from [unknown source location].
- End list.
The int-sans-ref deleted.
Known allocated blocks:
- End list.
MyClass(char * c)
{
a = new char[10];
strcpy(a, c);
throw here ...
}
~MyClass()
{
delete[] a;
}
struct Janitor {
Janitor(char* item) : mgr(item) {}
~Janitor() { if uncaught_exception() delete [] mgr; }
char *mgr;
};
MyClass(char * c)
{
Janitor j(new char[10]);
// j is destroyed both if the rest of the contstructor succeeds
// and if it throws
//unsafe code
strcpy(j.mgr, c);
...
//at the end
//pass the resource management to MyClass
a = j.mgr;
};