Перегружены новые проблемы оператора - PullRequest
8 голосов
/ 22 июля 2011

Я решил перегрузить новые, новые операторы [], ... в моих классах, чтобы я мог регистрировать файл и строку, в которой они были вызваны, чтобы я мог легче отслеживать выделения / утечки памяти.

Теперь проблема в моих классах стека и массива (и других шаблонных классов-контейнеров, которые выделяют память):

Если я использую их с одним из моих классов, у которого перегружены операторы new, new [], ..., он работает нормально.

Но если я использую его со стандартными типами данных c ++ (int, float, ...), я не могу выделить их, поскольку ни один перегруженный оператор new не соответствует аргументам нового (__ LINE __, __ FILE __) оператор (или другие, такие как размещение новых).

Пример кода стека:

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

Так что у меня нет хороших идей о том, как заставить это работать. Если я заменяю новый (__LINE __, __ FILE __) новым, я теряю возможность регистрации в памяти. Одним из решений является создание отдельного стека для стандартных типов данных, в которых используется новый по умолчанию.

Есть ли способ обнаружить во время компиляции, является ли параметр шаблона структурой, классом или встроенным типом c ++?

Как вы справляетесь с такими вещами? Что ты предлагаешь? Любые комментарии к этому дизайну (хорошие, плохие), безусловно, приветствуются (просто не публикуйте такие вещи, как «не изобретайте колесо с вашими собственными контейнерами»).

Ответы [ 3 ]

1 голос
/ 17 августа 2011

Обратите внимание, что текущее решение требует добавления кода регистрации в при каждой перегрузке 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 обладает этой функциональностью, см. здесь
- Существует общее количество по поводу отслеживания памяти таким способом здесь в разделе «Отслеживание утечек памяти»

0 голосов
/ 22 июля 2011

Есть ли способ обнаружить во время компиляции, является ли параметр шаблона структурой, классом или встроенный тип 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 для вашего набора типов

0 голосов
/ 22 июля 2011
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 является чем-то черным, и обычно ее следует избегать.

...