mmu*_*phy 5 c++ memory memory-management placement-new new-operator
我一直在努力创建一个自定义分配器作为一个有趣的练习/练习,我遇到了创建数组的两个潜在问题.对于典型的分配调用,我将使用malloc和placement new.但是,当我去创建一个数组时,我对它应该如何完成感到困惑.有一次,我注意到在某些地方,这里的placement new数组似乎可能不安全.在尝试使用数组时,我也遇到了自己的错误.我会得到错误的placement new
`错误C2679:binary'=':找不到运算符,它采用'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);
}
\n\n\n1) 分配器如何在不使用 new[] 的情况下创建数组?是新的安置吗?如果是这样,我上面发布的链接中提到的潜在危险又如何呢?
\n
链接中的问题是误解了事情的运作方式。每个实现都有一个实现定义的方法来记录有关已分配数组的信息。单个对象不需要此信息,因为它是由客户端通过delete.
对于数组,实现必须记录诸如元素计数、要调用的析构函数(如果适用)、元素大小\xe2\x80\xa6 等内容,这些内容通常存储在返回分配的开头,并且实现显然会偏移大小适当地分配请求。因此,实际大小会发生偏移,以便容纳这些隐藏值。这就是为什么除非你的分配器进行额外的簿记(顺便说一句,和集合接口带来的),malloc(sizeof...)否则它将不起作用。std::allocator
要正确记录此信息,您可以定义static void* operator new[]. 要通过放置将您自己的分配器合并到此方案中,您可以使用以下方法:
// quick/dirty/incomplete illustration:\n#include <stdio.h>\n#include <new>\n#include <cstdlib>\n\nclass t_allocator {\npublic:\n    t_allocator() {\n    }\n\n    ~t_allocator() {\n    }\n\npublic:\n    void* allocate(const size_t& size) {\n        return malloc(size);\n    }\n};\n\nclass SomeClass {\npublic:\n    SomeClass() {\n        printf("Constructed\\n");\n    }\n\n    ~SomeClass() {\n        printf("Destructed\\n");\n    }\n\npublic:\n    static void* operator new[](size_t size, t_allocator& allocator) {\n        return allocator.allocate(size);\n    }\n\n    /* in case static void* operator new[](size_t size, t_allocator& allocator) throws: */\n    static void operator delete[](void* object, t_allocator& allocator) {\n        /* ... */\n    }\n\n    static void operator delete[](void* object) {\n        /* matches t_allocator::allocate */\n        free(object);\n    }\n};\n\nint main(void) {\n    t_allocator allocator;\n    SomeClass* t(new (allocator) SomeClass[2]);\n\n    delete[] t;\n    t = 0;\n\n    return 0;\n}\n请注意,如果您的分配器可能抛出异常,您将类似地实现放置operator delete[]。
如果你想让你的分配器做一些簿记,事情就会变得混乱。就我个人而言,我认为该语言没有很好地实现这种情况,特别是因为数组初始化没有很好地实现。总会有一个额外的步骤来执行附近的建设或破坏,或者在这种情况下使用一些全球可访问的数据。
\n\n\n\n\n2)如果我想使用placement new并在数组中的每个元素上调用它,为什么我会收到上面提到的错误?
\n
如果您要创建不经过operator new/的分配器,则需要显式构造元素operator new[]。扩展上面的示例,您需要一个 destroy 方法,该方法调用delete[],然后告诉this释放/重用内存(而不是使用free上面的)。
如果您只是想要一个快速解决方案,则需要使用分配或分配器来处理析构函数、大小和元素计数。在这种情况下,您不使用new[]/ delete[]。
编辑
\n\n如果您想自己管理书籍,这里有一种方法(可以有很多方向):
\n\n#include <cassert>\n#include <stdio.h>\n#include <new>\n#include <cstdlib>\n\nclass t_allocator {\npublic:\n  t_allocator() {\n  }\n\n  ~t_allocator() {\n  }\n\npublic:\n  /** tracks an array allocation\'s data. acts as a scope container for the allocation/types. */\n  class t_array_record {\n  public:\n    typedef void (*t_destructor)(void* const);\n\n    template<typename T>\n    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) {\n      assert(this->d_mem);\n      /* mind exceptions */\n      char* const cptr(reinterpret_cast<char*>(this->d_mem));\n\n      for (size_t idx(0); idx < this->d_count; ++idx) {\n        /* assignment not required here: */\n        new (&cptr[this->d_size * idx]) T();\n      }\n\n      outObjects = reinterpret_cast<T*>(this->d_mem);\n    }\n\n    ~t_array_record() {\n      assert(this->d_mem);\n      char* const cptr(reinterpret_cast<char*>(this->d_mem));\n\n      for (size_t idx(0); idx < this->d_count; ++idx) {\n        const size_t element(this->d_count - idx - 1U);\n        this->d_destructor(& cptr[this->d_size * element]);\n      }\n\n      this->d_allocator.free(this->d_mem);\n    }\n\n  private:\n    template<typename T>\n    static void Destruct(void* const ptr) {\n      T* const obj(reinterpret_cast<T*>(ptr));\n\n      obj->~T();\n    }\n\n  private:\n    void* const d_mem;\n    t_destructor d_destructor;\n    const size_t d_size;\n    const size_t d_count;\n    t_allocator& d_allocator;\n  public:\n    t_array_record(const t_array_record&);\n    t_array_record& operator=(const t_array_record&);\n  };\npublic:\n  void* allocate(const size_t& size, const size_t& count) {\n    return malloc(size * count);\n  }\n\n  void free(void* const mem) {\n    ::free(mem);\n  }\n};\n演示:
\n\nclass SomeClass {\npublic:\n  SomeClass() {\n    printf("Constructed\\n");\n  }\n\n  virtual ~SomeClass() {\n    printf("Destructed\\n");\n  }\n\n  virtual void greet() {\n    printf("hi: %p\\n", this);\n  }\n\nprivate:\n  SomeClass(const SomeClass&);\n  SomeClass& operator=(const SomeClass&);\n};\n\nclass SomeDer : public SomeClass {\n  static int& N() {\n    static int a(0);\n\n    return ++a;\n  }\n\npublic:\n  SomeDer() : d_number(N()) {\n    printf("Ctor-%i\\n", this->d_number);\n  }\n\n  virtual ~SomeDer() {\n    printf("~Der%i-", this->d_number);\n  }\n\n  virtual void greet() {\n    printf("Der%i-", this->d_number);\n    SomeClass::greet();\n  }\n\nprivate:\n  const int d_number; /* << so we have different sized types in the example */\n};\n\ntemplate<typename T>\nvoid TryIt(const size_t& count) {\n  t_allocator allocator;\n\n  T* things(0);\n  t_allocator::t_array_record record(things, allocator, count);\n\n  for (size_t idx(0); idx < count; ++idx) {\n    things[idx].greet();\n  }\n}\n\nint main() {\n  TryIt<SomeClass>(3);\n  TryIt<SomeDer>(9);\n  return 0;\n}\n