C ++: новый вызов, который ведет себя как calloc? - PullRequest
37 голосов
/ 30 апреля 2009

Можно ли позвонить на new, чтобы обнулить память, например calloc?

Ответы [ 11 ]

73 голосов
/ 30 апреля 2009

Вопреки тому, что некоторые говорят в своих ответах, это возможно возможно.

char * c = new char[N]();

Инициализирует с нуля все символы (в действительности это называется инициализацией значения. Но инициализация значения будет инициализацией с нуля для всех его членов массива скалярного типа). Если это то, что вы после.

Стоит отметить, что он также работает для (массивов) типов классов без объявленного пользователем конструктора, и в этом случае любой из них является инициализированным значением:

struct T { int a; };
T *t = new T[1]();
assert(t[0].a == 0);
delete[] t;

Это не какое-то расширение или что-то в этом роде. Он работал и вел себя так же, как в C ++ 98. Просто там это называлось инициализация по умолчанию вместо инициализации значения. Однако инициализация нуля выполняется в обоих случаях для скаляров или массивов скалярных типов или типов POD.

11 голосов
/ 30 апреля 2009

Нет, но довольно легко создать новую версию, которая действует как calloc. Это можно сделать почти так же, как и в новой версии без бросков.

SomeFile.h

struct zeromemory_t{};
extern const zeromemory_t zeromemory;
void* __cdcel operator new(size_t cbSize, const zeromemory_t&);

SomeFile.cpp

const zeromemory_t zeromemory;

void* _cdecl operator new(size_t cbSize, const zeromemory_t&)
{
    void *mem = ::operator new(cbSize);
    memset(mem,0,cbSize);
    return mem;
}

Теперь вы можете сделать следующее, чтобы получить новый с нулевой памятью

MyType* pMyType = new (zeromemory) MyType();

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

5 голосов
/ 30 апреля 2009

Нет. Также даже не думайте делать что-то вроде:

YourClass *var = new YourClass;
memset(var, 0, sizeof(YourClass));

В итоге вы можете уничтожить свой VTABLE (если у вашего класса есть такой).

Я бы рекомендовал использовать конструкторы для очистки внутренней памяти (переменных) вашего класса.

2 голосов
/ 30 апреля 2009

Вы можете выполнить глобальную перегрузку оператора new и заставить его извлечь необработанную память из calloc(). Таким образом, память очищается перед запуском конструкторов, поэтому проблем нет.

Любой класс, который переопределяет новый самостоятельно, не получит ваш специальный new на основе *1006*, но тогда этот класс должен все равно правильно инициализироваться.

Не забудьте переопределить new и delete и версии массива ...

Что-то вроде:

#include <exception> // for std::bad_alloc
#include <new>
#include <stdlib.h> // for calloc() and free()

void* operator new (size_t size)
{
 void *p=calloc(size, 1); 
 if (p==0) // did allocation succeed?
  throw std::bad_alloc(); 
 return p;
}


void operator delete (void *p)
{
 free(p); 
}

void* operator new[] (size_t size)
{
 void *p=calloc(size, 1); 
 if (p==0) // did allocation succeed?
  throw std::bad_alloc();
 return p;
}

void operator delete[] (void *p)
{
 free(p); 
}

Обратите внимание, что эти простые версии не совсем такие, какими они должны быть - оператор new должен запускаться в цикле, вызывая new_handler (если он установлен) и выбрасывать исключение bad_alloc только при наличии нет new_handler. Или что-то в этом роде, мне придется поискать его и обновить позже.

О, и вы также можете переопределить версию no_throw.

2 голосов
/ 30 апреля 2009

Неа. Он всегда будет инициализировать по умолчанию выделенный элемент (ы), что в случае с примитивами ничего не делает. Вам нужно будет выполнить это с помощью вызова std :: uninitialized_fill_n или аналогичного.

1 голос
/ 13 июня 2011

я использую макрос:

#define newclear(TYPE) new(calloc(sizeof(TYPE), 1)) TYPE();

чтобы использовать его:

Whatever* myWhatever = newclear(Whatever);

(здесь используется «размещение нового», как и некоторые другие решения)

0 голосов
/ 30 апреля 2009

Вы можете сказать:

vector <char> v( 100, 0 );

, который создает непрерывный массив из 100 символов, используя new, и инициализирует их все в ноль. Затем вы можете получить доступ к массиву с помощью оператора [] вектора или выполнив:

char * p = &v[0];
p[3] = 42;

Обратите внимание, что это также освобождает вас от необходимости вызывать команду delete для освобождения выделенной памяти.

0 голосов
/ 30 апреля 2009
class MyClass {
    public:
    void* operator new(size_t bytes) {
        return calloc(bytes, 1);
    }
}

И вы можете переопределить глобальный новый оператор, если хотите.

0 голосов
/ 30 апреля 2009

Да.

int* p_scalar = new int(5);//Does not create 5 elements, but initializes to 5

Для массивов вы можете использовать что-то вроде memset. Для окон используйте ZeroMemory или SecureZeroMemory.

Редактировать: Пожалуйста, смотрите пост @ litb, он показывает, как вы можете инициализировать в 0 для массивов, используя прямую инициализацию, как описано выше.

0 голосов
/ 30 апреля 2009

если вы не настаиваете на использовании new, вы можете просто использовать vector: vector<char> buffer; buffer.resize(newsize); и содержимое будет обнулено.

...