Имитация new [] с помощью конструктора аргументов - PullRequest
5 голосов
/ 20 мая 2011

Если я не изменяю какую-либо переменную static внутри конструктора аргумента, ниже правильного способа симуляции new T[N] (x,y); (массив, новый с аргументами)?

template<typename T>
void* operator new [] (size_t size, const T &value)
{
  T* p = (T*) malloc(size);
  for(int i = size / sizeof(T) - 1; i >= 0; i--)
    memcpy(p + i, &value, sizeof(T));
  return p;
}

Использование будет,

struct A
{
  A () {}  // default
  A (int i, int j) {} // with arguments
};

int main ()
{
  A *p = new(A(1,2)) A[10];  // instead of new A[10](1,2)
}

Ответы [ 5 ]

5 голосов
/ 20 мая 2011

Я бы предложил

 std::vector<A> v(10, A(1,2));

Я понимаю, что это на самом деле не решает вопрос о массивах.Вы можете использовать

 p = &v[0]; 

, поскольку стандарт гарантирует непрерывное хранение.Будьте очень осторожны с изменением размера вектора, потому что это может сделать недействительным p

Я проверил boost :: array <> (который адаптирует массивы в стиле C), но он не определяет конструкторы ...

4 голосов
/ 20 мая 2011

Это не ОК.Вы копируете объекты в неинициализированную память без вызова правильной семантики копирования.

Пока вы работаете только с POD, это нормально.Однако при работе с объектами, которые не являются POD (например, A), необходимо соблюдать меры предосторожности.

Кроме того, operator new не может использоваться таким образом.Как указал Александр в комментариях, массив не будет инициализирован должным образом, поскольку C ++ будет вызывать конструкторы для всех элементов после вызова вашего operator new, переопределяя значения:

#include <cstdlib>
#include <iostream>

template<typename T>
void* operator new [] (size_t size, T value) {
    T* p = (T*) std::malloc(size);
    for(int i = size / sizeof(T) - 1; i >= 0; i--)
        new(p + i) T(value);
    return p;
}

struct A {
    int x;
    A(int x) : x(x) { std::cout << "int ctor\n"; }
    A() : x(0) { std::cout << "default ctor\n"; }
    A(const A& other) : x(other.x) { std::cout << "copy ctor\n"; }
};

int main() {
    A *p = new(A(42)) A[2];
    for (unsigned i = 0; i < 2; ++i)
        std::cout << p[i].x << std::endl;
}

Это дает:

int ctor
copy ctor
copy ctor
default ctor
default ctor
0
0

… не желаемый результат.

2 голосов
/ 20 мая 2011

Это не нормально - C ++ будет вызывать эти объекты нетривиальными конструкторами по умолчанию, если typename T имеет такие (у struct A в вашем примере есть), и это приведет к восстановлению объектов в памяти, уже занятой.

Подходящим решением было бы использовать std::vector (рекомендуется) или вызвать ::operator new[] для выделения памяти, затем вызывать конструкторы с использованием размещения нового и заботиться об исключениях, если таковые имеются.

1 голос
/ 20 мая 2011

Следует учитывать, что operator new[] может вызываться с запросом большего количества памяти, чем необходимое количество sizeof(T) * n.

Эта дополнительная память, возможно, необходима, потому что C ++ должен знать, сколько объектов нужно уничтожить в случае delete[] p;, но он не может надежно использовать размер блока памяти, выделенного new p[sz], чтобы вывести это число, потому что память, возможно, была запрошена в специализированный диспетчер памяти, поэтому (например, ваш случай) нет способа узнать, сколько памяти былораспределяется только путем знания указателя.

Это также означает, что ваша попытка предоставить уже инициализированные объекты потерпит неудачу, потому что фактически массив, возвращенный приложению, потенциально не будет начинаться с адреса, который вы вернули из своего пользовательского operator new[]чтобы инициализация могла быть смещена.

0 голосов
/ 20 мая 2011
template <typename myType> myType * buildArray(size_t numElements,const myType & startValue) {
  myType * newArray=(myType *)malloc(sizeof(myType)*numElements);

  if (NULL!=newArray) {
    size_t index;
    for (index=0;index<numElements;++index) {
      new (newArray+index) myType(startValue);
    }
  }

  return newArray;
}

template <typename myType> void destroyArray(size_t numElements,myType * oldArray) {
  size_t index;
  for (index=0;index<numElements;++index) {
    (oldArray+index)->~myType();
  }
  free(oldArray);
}

A * p=newArray(10,A(1,2));
destroyArray(10,p);

destroyArray также может быть написано так в зависимости от платформы, для которой вы строите:

template <typename myType> void destroyArray(myType * oldArray) {
  size_t numElements=malloc_size(oldArray)/sizeof(myType); //or _msize with Visual Studio
  size_t index;
  for (index=0;index<numElements;++index) {
    (oldArray+index)->~myType();
  }
  free(oldArray);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...