【问题标题】:Overloaded new operator problems重载新操作员问题
【发布时间】:2011-10-10 22:27:21
【问题描述】:

我决定在我的类中重载 new、new[]、... 运算符,以便记录调用它们的文件和行,以便更轻松地跟踪内存分配/泄漏。

现在问题出在我的堆栈和数组类(以及其他分配内存的模板容器类)中:

如果我将它们与我的一个类一起使用,它有 new,new[],... 运算符重载,它可以正常工作。

但如果我将它与标准 c++ 数据类型(int、float、...)一起使用,我无法分配它们,因为没有重载的 new 运算符与 new(__ LINE __ , __ FILE __) 的参数匹配运算符(或其他类似placement new)。

堆栈代码示例:

// placement new
T* t=new(__ LINE __ , __ FILE__)(&m_data[i])T;

所以我对如何完成这项工作没有好主意。如果我用 new 替换 new(__ LINE __ ,__ FILE __) 我失去了内存记录能力。 一种解决方案是为使用默认 new 的标准数据类型创建一个单独的堆栈。

有什么方法可以在编译时检测模板参数是结构、类还是内置 c++ 类型?

你如何处理这样的事情? 你有什么建议? 任何关于这种设计(好的,坏的)的 cmets 显然都是受欢迎的(只是不要发布诸如“不要用你自己的容器重新发明轮子”之类的东西)。

【问题讨论】:

  • 您是否在全局范围内重载operator new(__LINE__, __FILE__) 并尝试与intfloat 等一起使用?
  • 一种选择是替换全局operator new() 和朋友。
  • 如果你正在寻找内存泄漏调试,我不明白你为什么要为此做一些已经存在的工作:Valgrind?
  • 也许this 有帮助?
  • 另外,有人可能会问为什么您需要动态分配原始数据类型...也许可以重新设计设计以减少动态存储?

标签: c++ memory new-operator memory-leaks operator-keyword


【解决方案1】:

请注意,您当前的解决方案需要将日志记录代码添加到每个 new(line, file) 过载。此外,除非您将每个日志调用都包含在 #ifndef DEBUG ... #endif 中,否则您无法在发布版本中轻松将其关闭。

这是实现您想要的目标的一种方法:不要为每个类重载new 运算符,而是考虑使用放置语法重载全局new 运算符;这样您就可以避免干扰“正常”new 运算符。然后,您可以方便地#define 新建和删除宏,最重要的是,您可以控制何时应用内存跟踪new/delete 以及何时使用标准版本。

#ifdef ENABLE_CUSTOM_ALLOC

// Custom new operator. Do your memory logging here.
void* operator new (size_t size, char* file, unsigned int line)
{
    void* x = malloc(size);
    cout << "Allocated " << size << " byte(s) at address " << x 
        << " in " << file << ":" << line << endl;
    return x;  
}

// You must override the default delete operator to detect all deallocations
void operator delete (void* p)
{
   free(p);
   cout << "Freed memory at address " << p << endl;
}

// You also should provide an overload with the same arguments as your
// placement new. This would be called in case the constructor of the 
// created object would throw.
void operator delete (void* p, char* file, unsigned int line)
{
   free(p);
   cout << "Freed memory at address " << p << endl;
}

#define new new(__FILE__, __LINE__)

#endif


// A test class with constructors and destructor
class X
{
public: 
    X() { cout << "X::ctor()" << endl; }
    X(int x) { cout << "X::ctor(" << x << ")" << endl; }
    ~X() { cout << "X::dtor()" << endl; }
};


int main (int argc, char* argv[])
{
    X* x3 = new X();
    X* x4 = new X(20);
    delete x3;
    delete x4;
}

你应该看到类似的东西:

Allocated 1 byte(s) at address 00345008 in Alloc.cpp:58
X::ctor()
Allocated 1 byte(s) at address 003450B0 in Alloc.cpp:59
X::ctor(20)
X::dtor()
Freed memory at address 00345008
X::dtor()
Freed memory at address 003450B0

尝试将X 替换为int,您会发现它也有效。您也可以将其扩展到数组和新位置,但我宁愿不要让帖子比它长。

最后几个指针:
- MSVC 有这个功能,见here
- 在“跟踪内存泄漏”部分下有一个关于以这种方式进行内存跟踪here 的教程

【讨论】:

    【解决方案2】:
    struct Int {
        int i;
        Int (int _i) : i(_i) {}
        operator int () const {return i;}
    };
    
    #define __LINE_NUMBER__ Int(__LINE__)
    

    使用此宏代替标准行号宏,重载解析将Int 行号与int 其他数字区分开来。

    我无法想象这将如何全面展开。你打算像int * x = NEW(int,123); 那样使用它吗?

    顺便说一句,我同意评论者的观点 - 您可能不必走这条路。重载new 是一种魔法,通常应该避免。

    【讨论】:

    • 我想你不明白我想要什么:LINE 宏没有问题。还是你想告诉我别的?
    • 问题是__LINE__ 产生一个干扰重载解析的int,对吗?这里我把它包装在一个类中,这样重载就不同了。
    【解决方案3】:

    有什么方法可以在编译时检测模板参数是结构、类还是 一个内置的 c++ 类型?

    你可以使用 boos::type_traits 和 boost::mpl 。

    例子:

    #include <boost/type_traits.hpp>
    #include <boost/mpl/not.hpp>
    #include <boost/mpl/logical.hpp>
    
    template <class T>
    typename boost::enable_if<boost::is_class<T>, T>::type
    foo(){cout << "is class " << endl;};
    
    template <class T>
    typename boost::enable_if<boost::mpl::not_<boost::is_class<T> >, T>::type
    foo(){cout << "is not class "<< endl;};
    

    类型列表 - http://www.boost.org/doc/libs/1_47_0/libs/type_traits/doc/html/index.html

    或者你可以使用 boost::mpl::set 作为你的类型集

    【讨论】:

    • 我仍在努力解决这个问题:在模板类中,我可以让编译器根据模板参数类型在编译时选择使用默认的 new 还是重载的 new (所以如果它是一个结构或类,它会选择重载的new,但如果是别的,它会选择默认的new?)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-13
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多