如何同时使用可能放置新创建一个数组(How to create an array while pot

2019-09-16 14:48发布

我一直在努力创建一个自定义分配器作为一个有趣的练习/练习,我跑了与创建阵列两个电位的问题。 对于一个典型的呼叫用于分配,我将使用mallocplacement new 。 然而,当我去创建一个数组,我就应该怎么做困惑。 这一次,我在,它似乎有些地方注意到placement new可能不是数组安全,如这里 。 我也遇到了自己的错误,而试图使用placement new的数组。 我会得到的错误

`错误C2679:二进制“=”:没有操作员发现这需要类型的右边的操作数“SomeClass的*”(或没有可接受的转化率)

我理解的错误(我认为),但我宁愿错误通过我的数组构造方法来解决。 我有两个问题

1)一个分配器如何创建,而无需使用一个数组new[] 它是与placement new ? 如果是这样,怎么样从我上面贴的链接中提到的潜在危险?

2)如果我想使用placement new ,并调用它的数组中的元素中的每一个,为什么我收到上面提到的错误?

#include <stdio.h>
#include <new>

class SomeClass{
public:
    SomeClass() {
        printf("Constructed\n");
    }

    ~SomeClass() {
        printf("Destructed\n");
    }
};

void* SomeAllocationFunction(size_t size) {
    return malloc(size);
}

template<typename Type>
Type* SomeArrayAllocationFunction(size_t count){
    Type* mem = (Type*)SomeAllocationFunction(sizeof(Type) * count);

    for(unsigned int i = 0; i < count; ++i)
    {
        mem[i] = new(mem + i) Type();
    }

    return mem; 
}

int main(void){
    SomeClass* t = SomeArrayAllocationFunction<SomeClass>(2);
}

Answer 1:

1)一个分配器如何创建,而无需使用新的[]数组? 它是与安置的新? 如果是这样,怎么样从我上面贴的链接中提到的潜在危险?

在链接的问题是误解是如何工作的。 各实施方案具有一个实现中定义的装置来记录所分配的阵列信息。 此信息不需要用单个对象,因为它是由客户端通过实施管理delete

对于数组,执行必须记录的事情,如元素计数,析构函数调用(如适用),元素大小......这些东西往往是存储在返回分配的开始,和实现明显适当地抵消了分配请求的大小。 因此,实际的大小,以便容纳这些隐藏的值偏移。 这就是为什么malloc(sizeof...)不会工作,除非你不分配额外的簿记(其中,顺便说一句, std::allocator和集合接口带来的)。

为了正确地记录这些信息,你可以定义static void* operator new[] 要通过安置纳入自己的分配器在这个方案中,你可以用下面的办法:

// quick/dirty/incomplete illustration:
#include <stdio.h>
#include <new>
#include <cstdlib>

class t_allocator {
public:
    t_allocator() {
    }

    ~t_allocator() {
    }

public:
    void* allocate(const size_t& size) {
        return malloc(size);
    }
};

class SomeClass {
public:
    SomeClass() {
        printf("Constructed\n");
    }

    ~SomeClass() {
        printf("Destructed\n");
    }

public:
    static void* operator new[](size_t size, t_allocator& allocator) {
        return allocator.allocate(size);
    }

    /* in case static void* operator new[](size_t size, t_allocator& allocator) throws: */
    static void operator delete[](void* object, t_allocator& allocator) {
        /* ... */
    }

    static void operator delete[](void* object) {
        /* matches t_allocator::allocate */
        free(object);
    }
};

int main(void) {
    t_allocator allocator;
    SomeClass* t(new (allocator) SomeClass[2]);

    delete[] t;
    t = 0;

    return 0;
}

请注意,您将同样实行安置operator delete[]如果你的分配器可能会抛出。

如果你希望你的分配做一些记录时,它就会变得混乱。 就个人而言,我不认为这种情况是由语言实现好,特别是因为数组初始化并没有落实到位。 总是会有执行近施工或破坏,或在这种情况下使用一些全局访问数据的额外步骤。

2)如果我想使用新的布局,并调用它的数组中的元素中的每一个,为什么我收到上面提到的错误?

你需要显式地构造元素,如果你正在创建一个allocator不通过走operator new / operator new[] 扩展上面的例子中,你会想破坏方法,要求delete[]然后告诉this自由/重用内存(而不是使用free以上)。

如果你只是想快速解决方案,无线本地环路需要挑着析构函数,大小和元素计数与周围的分配或分配。 在scenarion,您不使用new[] / delete[]

编辑

如果你想自己管理的书,这里有一个方法(可以去很多方向):

#include <cassert>
#include <stdio.h>
#include <new>
#include <cstdlib>

class t_allocator {
public:
  t_allocator() {
  }

  ~t_allocator() {
  }

public:
  /** tracks an array allocation's data. acts as a scope container for the allocation/types. */
  class t_array_record {
  public:
    typedef void (*t_destructor)(void* const);

    template<typename T>
    t_array_record(T*& outObjects, t_allocator& allocator, const size_t& count) : d_mem(allocator.allocate(sizeof(T), count)), d_destructor(t_allocator::t_array_record::Destruct<T>), d_size(sizeof(T)), d_count(count), d_allocator(allocator) {
      assert(this->d_mem);
      /* mind exceptions */
      char* const cptr(reinterpret_cast<char*>(this->d_mem));

      for (size_t idx(0); idx < this->d_count; ++idx) {
        /* assignment not required here: */
        new (&cptr[this->d_size * idx]) T();
      }

      outObjects = reinterpret_cast<T*>(this->d_mem);
    }

    ~t_array_record() {
      assert(this->d_mem);
      char* const cptr(reinterpret_cast<char*>(this->d_mem));

      for (size_t idx(0); idx < this->d_count; ++idx) {
        const size_t element(this->d_count - idx - 1U);
        this->d_destructor(& cptr[this->d_size * element]);
      }

      this->d_allocator.free(this->d_mem);
    }

  private:
    template<typename T>
    static void Destruct(void* const ptr) {
      T* const obj(reinterpret_cast<T*>(ptr));

      obj->~T();
    }

  private:
    void* const d_mem;
    t_destructor d_destructor;
    const size_t d_size;
    const size_t d_count;
    t_allocator& d_allocator;
  public:
    t_array_record(const t_array_record&);
    t_array_record& operator=(const t_array_record&);
  };
public:
  void* allocate(const size_t& size, const size_t& count) {
    return malloc(size * count);
  }

  void free(void* const mem) {
    ::free(mem);
  }
};

演示:

class SomeClass {
public:
  SomeClass() {
    printf("Constructed\n");
  }

  virtual ~SomeClass() {
    printf("Destructed\n");
  }

  virtual void greet() {
    printf("hi: %p\n", this);
  }

private:
  SomeClass(const SomeClass&);
  SomeClass& operator=(const SomeClass&);
};

class SomeDer : public SomeClass {
  static int& N() {
    static int a(0);

    return ++a;
  }

public:
  SomeDer() : d_number(N()) {
    printf("Ctor-%i\n", this->d_number);
  }

  virtual ~SomeDer() {
    printf("~Der%i-", this->d_number);
  }

  virtual void greet() {
    printf("Der%i-", this->d_number);
    SomeClass::greet();
  }

private:
  const int d_number; /* << so we have different sized types in the example */
};

template<typename T>
void TryIt(const size_t& count) {
  t_allocator allocator;

  T* things(0);
  t_allocator::t_array_record record(things, allocator, count);

  for (size_t idx(0); idx < count; ++idx) {
    things[idx].greet();
  }
}

int main() {
  TryIt<SomeClass>(3);
  TryIt<SomeDer>(9);
  return 0;
}


Answer 2:

mem[i]的类型为Type&new(mem + i) Type(); 有型Type* 。 这些都是明显不符的类型,不能进行分配。 我相信你可以完全删除分配,它会工作了,仍然在初始化你该位置的记忆。

我还是稍微谨慎实施,虽然自己的阵列分配子(自定义分配器vector将是例如更明显)。



文章来源: How to create an array while potentially using placement new