【问题标题】:c++ std::make_shared with new macroc++ std::make_shared 与新宏
【发布时间】:2014-09-20 13:45:04
【问题描述】:

我使用宏代替new 在调试模式下获取一些额外信息:

#if defined(_DEBUG)
#define SAGE_NEW new(__FUNCTION__, __FILE__, __LINE__)
#else
#define SAGE_NEW new
#endif

我发现这在自定义内存分析和内存泄漏检测中非常有用。我刚开始使用共享指针,所以现在我正在制作堆对象,例如:

auto myThingy = std::shared_ptr<Thingy>(SAGE_NEW Thingy(Args) );

我刚刚了解到std::make_shared 是首选,因为它使用较少的内存分配。有什么办法可以将我的SAGE_NEW 包含在make_shared 中?我意识到泄漏检测并不重要,但我仍然希望它用于内存使用统计。似乎allocate_shared 以某种方式掌握了答案,但我还没有弄清楚。谢谢! :)

编辑: 对于那些询问new 的人-我用自定义new 重载它。编译器选项 SAGE_MEM_INFO 打开泄漏检测和内存使用统计信息,否则它会跳过日志记录并直接进入我的内存池分配。我有 new[] 和 delete[] 变体,但为简洁起见,我省略了它们:

#ifdef SAGE_MEM_INFO
void* operator new  (size_t size){ ++appAllocs; return myAlloc(size); }
void* operator new  (size_t size, char const *function, char const *filename, int lineNum)
{
    ... Log memory usage
    void* ptr = ::operator new(size);
    return ptr;
}
void  operator delete   (void* ptr)
{
    ... Log freeing of this pointer
    --appAllocs;
    myFree(ptr);
}
void  operator delete   (void* ptr, char const *function, char const *filename, int lineNum)
{
    ... Log freeing of this pointer
    --appAllocs;
    myFree(ptr);
}
#else
void* operator new  (size_t size){ return myAlloc(size); }
void* operator new  (size_t size, char const *function, char const *filename, int lineNum)
{
    void* ptr = ::operator new(size);
    return ptr;
}
void  operator delete  (void* ptr) { myFree(ptr); }
void  operator delete  (void* ptr, char const *function, char const *filename, int lineNum) { myFree(ptr); } 
#endif

【问题讨论】:

  • 快速评论:人们经常提到 make_shared 的内存分配。这是真的,但这是一次性成本(每个指针)。另一个很好的理由是,当您使用 make_shared 时,对象本身及其引用计数(可能还有其他内容)都被放入一个连续的内存块中。这会为复制和删除副本带来更好的缓存行为,每个创建的指针可能会重复多次。
  • 如果您有解决方案,请将其写为答案。问题框是为,嗯,你的问题
  • 对于您的原始宏,如上所示,您需要记住定义一个相应的放置operator delete,因为如果构造函数抛出,则放置新表达式会调用它。没有它,您会遇到内存泄漏,这是您要消除的东西。这曾经是一个臭名昭著的 MFC 错误。
  • 我从未见过像new(__FUNCTION__, __FILE__, __LINE__) 这样的语法。你能给我指出发生了什么的参考吗? cplusplus.com/reference/new/operator%20new 没有提到为调试做类似的事情。你在接受额外参数的地方有一个重载的 operator new 吗?

标签: c++ c++11 make-shared


【解决方案1】:

是的,你当然可以这样做。

不过,你必须选择你的毒药:

  • 使用不为空但至少保存一个指针的分配器类型。
  • 为每个分配使用新的分配器类型,这将反映在引用计数的新多态类型中。

http://en.cppreference.com/w/cpp/concept/Allocator 显示了需求,以及良好的最小分配器声明。

改编std::allocator这里作为第一个选项:

#if defined(_DEBUG)
template <class Tp>
struct DebugLinesAllocator : std::allocator<Tp> {
  const char* func, *file;
  int line;
  Tp* allocate(std::size_t n, const void* = 0)
  {return ::operator new(n * sizeof(T), func, file, line);}
  template< class U > struct rebind { typedef DebugLinesAllocator<U> other; };
  DebugLinesAllocator(const char* func, const char* file, int line)
  : func(func), file(file), line(line) {}
  template< class U > DebugLinesAllocator( const DebugLinesAllocator<U>& other )
  : func(other->func), file(other->file), line(other->line) {}
};
#define SAGE_MAKE_SHARED(type, ...) allocate_shared<type>(DebugLinesAllocator<type>\
    {__FUNCTION__, __FILE__, __LINE__}, __VA_ARGS__)
#else
#define SAGE_MAKE_SHARED(type, ...) make_shared<type>(__VA_ARGS__)
#endif

不过,它对共享指针的用处要小得多。无论如何,每一点都可能有所帮助。

像这样使用它

auto p = SAGE_MAKE_SHARED(my_type, my_argument_1, ...);

【讨论】:

  • C++11 中不需要rebind
  • @Puppy:不过,基类可能有它,所以我最好也有它。 (可能记得有几次我不添加到std::allocator
  • 谢谢。似乎有效,但我添加了一个 static_cast,明确编写了 FUNCTION 的类型等。请参阅我编辑的答案。再次感谢!
  • @DSM:添加 rebind+ctor 很晚,忘了在那里添加复杂的 ctor。其余的,......不知道。
  • 使用自定义分配函数(placement new 运算符)进行分配,最好也使用相应的解除分配函数。通常,像这里这样的调试分配的实现是将信息存储在比请求稍大的块的开头,然后返回指向信息后面部分的指针。普通的释放函数将无法处理该问题(否则它将无法处理普通的分配块,无论哪种方式都不好)。
【解决方案2】:

您可以创建自己的makeShared,您可以在其中进行任何您需要的簿记,然后您将调用真正的make_shared

【讨论】:

    【解决方案3】:

    发布答案而不是问题...

    我的编译器不喜欢 Deduplicator 的回答中的某些内容。下面似乎并没有完全错误:

    template <class Tp>
    struct DebugLinesAllocator : std::allocator<Tp> {
        char const * func;
        char const * file;
        int line;
        DebugLinesAllocator(char const * aFunc, char const * aFile, const int aLine) : func(aFunc), file(aFile), line(aLine) {}
        Tp* allocate(std::size_t n, const void* = 0)
        {
            return static_cast<Tp*> (::operator new(n * sizeof(Tp), func, file, line));
        }
        template< class U > struct rebind { typedef DebugLinesAllocator<U> other; };
        template< class U > DebugLinesAllocator(const DebugLinesAllocator<U>& other)
            : func(other.func), file(other.file), line(other.line) {}
    };
    #if defined(_DEBUG)
    #define SAGE_MAKE_SHARED(type, ...) std::allocate_shared<type>(DebugLinesAllocator<type>(__FUNCTION__, __FILE__, __LINE__), __VA_ARGS__)
    #else
    #define SAGE_MAKE_SHARED(type, ...) std::make_shared<type>(__VA_ARGS__)
    #endif
    

    如果您认为我的任何更改值得怀疑,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-28
      • 1970-01-01
      • 2021-06-05
      • 2014-08-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多