5

私は楽しい演習/練習としてカスタム アロケーターの作成に取り組んできましたが、配列の作成に関して 2 つの潜在的な問題に遭遇しました。割り当ての典型的な呼び出しでは、 と を使用mallocplacement newます。ただし、配列を作成しようとすると、どのように行うべきか混乱しています。ここで、 hereplacement newのような配列に対して安全ではない可能性があることに気付きました。また、配列に使用しようとしているときに、自分自身のエラーが発生しています。のエラーが発生しますplacement new

「エラー C2679: バイナリ '=' : 'SomeClass *' 型の右側のオペランドを取る演算子が見つかりません (または、受け入れ可能な変換がありません)

私はエラーを理解しています(私は信じています)が、配列構築方法でエラーを解決したいと思っています。2つの質問があります

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);
}
4

2 に答える 2

1

mem[i]has typeType&ながらnew(mem + i) Type();has type Type*. これらは明らかに互換性のない型であり、割り当てることはできません。割り当てを完全に削除するとうまくいくと思いますが、その場所でメモリを初期化します。

ただし、独自の配列アロケーターを実装することにはまだ少し注意が必要です (vectorたとえば、カスタム アロケーターはより明白です)。

于 2012-04-19T04:30:24.870 に答える
1

1) new[] を使用せずにアロケータで配列を作成するにはどうすればよいですか? プレースメント付き新品ですか?もしそうなら、私が上に投稿したリンクから言及された潜在的な危険性はどうですか?

リンクの問題は、物事がどのように機能するかを誤解していることです。各実装には、割り当てられた配列に関する情報を記録する手段が定義されています。この情報は、 の実装を介してクライアントによって管理されるため、単一のオブジェクトでは必要ありません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) 配置 new を使用して、配列内の各要素に対してそれを呼び出すとしたら、なぜ上記のエラーが発生するのですか?

operator new/を通過しないアロケーターを作成する場合は、要素を明示的に構築する必要がありますoperator new[]。上記の例を拡張すると、を呼び出す destroy メソッドが必要になり、(上記の使用ではなく) メモリを解放/再利用するようにdelete[]指示されます。thisfree

迅速な解決策が必要な場合は、デストラクタ、サイズ、および要素数をアロケーションまたはアロケータと一緒に移動する必要があります。new[]そのシナリオでは、 /を使用しませんdelete[]

編集

また、書籍を自分で管理したい場合は、次の 1 つの方法を使用できます (さまざまな方向に進む可能性があります)。

#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;
}
于 2012-04-19T05:33:30.843 に答える