C++: новый вызов, который ведет себя как calloc?

Это было раскрыто в примечаниях к выпуску, когда вывод типа из условных типов был введен. Будет ли это объединение или пересечение, зависит от дисперсии предполагаемого типа. Интуитивно понятно, что если тип выводится как общий тип для нескольких значений, он может быть любым из них (объединение), но если он выводится как тип аргумента для нескольких функций, он должен быть приемлемым для любой из них (пересечение). [116 ]

Цитата из примечаний к выпуску:

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

blockquote>
type Foo = T extends { a: infer U, b: infer U } ? U : never;
type T10 = Foo<{ a: string, b: string }>;  // string
type T11 = Foo<{ a: string, b: number }>;  // string | number

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

blockquote>
type Bar = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>;  // string
type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>;  // string & number

Дополнительное обсуждение можно найти в PR реализует эту функцию.

В качестве примечания, он позволяет использовать некоторые интересные трюки, такие как объединение в пересечение типов пересечения .

37
задан banarun 1 July 2013 в 07:44
поделиться

10 ответов

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

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

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

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

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

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

73
ответ дан 27 November 2019 в 04:20
поделиться

Нет, но довольно легко создать новую версию, которая действует как 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();

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

11
ответ дан 27 November 2019 в 04:20
поделиться

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

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

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

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

5
ответ дан 27 November 2019 в 04:20
поделиться

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

2
ответ дан 27 November 2019 в 04:20
поделиться

Нет. Необходимо вручную обнулить память. Помните, что new - это не только выделение памяти, но и инициализация с помощью конструкторов. Именно здесь calloc удобен в C (который не имеет функций инициализатора). Вы можете написать оболочку над new или даже использовать calloc , но большую часть времени для объектов без POD это не имеет особого смысла.

0
ответ дан 27 November 2019 в 04:20
поделиться

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

0
ответ дан 27 November 2019 в 04:20
поделиться

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

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

Не забудьте переопределить 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
ответ дан 27 November 2019 в 04:20
поделиться
class MyClass {
    public:
    void* operator new(size_t bytes) {
        return calloc(bytes, 1);
    }
}

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

0
ответ дан 27 November 2019 в 04:20
поделиться

Да.

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

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

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

0
ответ дан 27 November 2019 в 04:20
поделиться

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

vector <char> v( 100, 0 );

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

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

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

0
ответ дан 27 November 2019 в 04:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: