C++ 根据新放置跟踪放置删除

C++ 根据新放置跟踪放置删除,c++,C++,我正在开发一个类似内存泄漏检测器的工具。我可以跟踪新位置,但如何跟踪位置删除。我做了很多研发,发现placement delete不能直接调用,它是在异常时由构造函数调用的。那么,如何根据新的放置跟踪放置删除 如果您有任何帮助,我们将不胜感激……如果您没有,我们将无需跟踪 Placement new意味着将对象放置在内存位置。已为其分配了存储空间。您不会对对象本身调用任何形式的delete,因为这是从何处获取存储的工作 也就是说,在这方面: void* memory = operator new

我正在开发一个类似内存泄漏检测器的工具。我可以跟踪新位置,但如何跟踪位置删除。我做了很多研发,发现placement delete不能直接调用,它是在异常时由构造函数调用的。那么,如何根据新的放置跟踪放置删除


如果您有任何帮助,我们将不胜感激……

如果您没有,我们将无需跟踪

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;
};
其中一些可能也有帮助。





还不完全清楚你想做什么。你说的“跟踪”是什么意思?那么您的意思是,当使用placement
new
构造对象时,您希望跟踪这一事实,并确保在回收内存之前显式调用其析构函数?+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;
};