3

私は C++ を使用しており、次の構造を持っています。

struct ArrayOfThese {
  int a;
  int b;
};

構造体データポイント {
  int a;
  int b;
  int c;
};

メモリ内では、各 DataPoint の最後に 1 つ以上の ArrayOfThese 要素が必要です。DataPoint ごとに常に同じ数の ArrayOfThese 要素があるとは限りません。

膨大な数の DataPoint を集めてネットワーク経由でストリーミングする必要があるため、すべての DataPoint とその ArrayOfThese 要素を連続させたいと考えています。固定数の ArrayOfThese 要素のためにスペースを浪費することは容認できません。

C では、DataPoint の最後に として宣言された要素を作成しArrayOfThese d[0];、DataPoint に加えて、多くの ArrayOfThese 要素に十分な余分なバイトを割り当て、ダミー配列を使用してそれらにインデックスを付けます。(もちろん、ArrayOfThese 要素の数は DataPoint のフィールドにある必要があります。)

C++ では、新しい配置と同じ長さ 0 の配列ハックを使用して正しいアプローチをとっていますか? もしそうなら、配置 new は、同じメモリプールからの new への後続の呼び出しが連続して割り当てられることを保証しますか?

4

11 に答える 11

5

コンストラクターを持たない単純な構造体を扱っているため、C メモリ管理に戻すことができます。

void *ptr = malloc(sizeof(DataPoint) + n * sizeof(ArrayOfThese));
DataPoint *dp = reinterpret_cast<DataPoint *>(ptr));
ArrayOfThese *aotp = reinterpet_cast<ArrayOfThese *>(reintepret_cast<char *>(ptr) + sizeof(DataPoint));
于 2009-10-26T19:46:36.633 に答える
3

構造体は POD であるため、C で行う場合と同様に行うことができます。必要なのはキャストだけです。nは、割り当てるものの数であると仮定します。

DataPoint *p=static_cast<DataPoint *>(malloc(sizeof(DataPoint)+n*sizeof(ArrayOfThese)));

オブジェクトに重要なコンストラクターがある場合、新しい配置はこの種のものになります。ただし、それ自体は割り当てられず、何らかの方法でメモリが既に割り当てられている必要があるため、割り当てについては何も保証しません。代わりに、渡されたメモリ ブロックをまだ構築されていないオブジェクトのスペースとして扱い、適切なコンストラクタを呼び出して構築します。これを使用する場合、コードは次のようになります。あなたが提案しDataPointたメンバーがいると仮定します:ArrayOfThese arr[0]

void *p=malloc(sizeof(DataPoint)+n*sizeof(ArrayOfThese));
DataPoint *dp=new(p) DataPoint;
for(size_t i=0;i<n;++i)
    new(&dp->arr[i]) ArrayOfThese;

構築されたものは破壊されなければならないので、これを行う場合は、デストラクタの呼び出しも整理する必要があります。

(個人的には、この種の状況では POD を使用することをお勧めします。これは、コンストラクターとデストラクタを呼び出す必要がなくなるためです。ただし、この種のことは、注意すれば合理的に安全に行うことができます。)

于 2009-10-26T19:53:26.510 に答える
2

エイドリアンが答えで言ったように、メモリ内で行うことは、ネットワーク経由でストリーミングすることと同じである必要はありません。実際、データを特定の方法で設計されたデータに依存する通信プロトコルを持つことは、後でデータをリファクタリングする必要がある場合に大きな問題になるため、これを明確に分割することをお勧めします。

任意の数の要素を連続して格納する C++ の方法は、もちろん tostd::vectorです。あなたはこれを考慮していなかったので、これを望ましくないものにしている何かがあると思います. (数が少なく、ArrayOfTheseそれに関連するスペースのオーバーヘッドが心配std::vectorですか?)

長さ 0 の配列を過剰に割り当てるというトリックは、おそらく動作することが保証されておらず、技術的には恐ろしい未定義の動作を引き起こす可能性がありますが、これは広く普及しています。どのプラットフォームを使用していますか? Windows では、これは Windows API で行われるため、これをサポートしない C++ コンパイラを出荷するベンダーを想像するのは困難です。

ArrayOfThese可能な要素数が限られている場合は、 fnieto のトリックを使用してこれらの数を指定newし、実行時の数に応じて結果のテンプレート インスタンスの 1 つを指定することもできます。

struct DataPoint {
  int a;
  int b;
  int c;
};

template <std::size_t sz>
struct DataPointWithArray : DataPoint {
  ArrayOfThese array[sz];
};

DataPoint* create(std::size_t n)
{
  switch(n) {
    case 1: return new DataPointWithArray[1];
    case 2: return new DataPointWithArray[2];
    case 5: return new DataPointWithArray[5];
    case 7: return new DataPointWithArray[7];
    case 27: return new DataPointWithArray[27];
    default: assert(false);
  }
  return NULL;
}
于 2009-10-26T20:47:17.913 に答える
1

DataPointにArrayOfThisアイテムの可変長配列を含めないのはなぜですか?これはCまたはC++で機能します。いずれかの構造体に非プリミティブ型が含まれている場合、いくつかの懸念があります

ただし、結果を削除するのではなく、free()を使用してください。

struct ArrayOfThese {
  int a;
  int b;
};


struct DataPoint {
  int a;
  int b;
  int c;
  int length;
  ArrayOfThese those[0];
};

DataPoint* allocDP(int a, int b, int c, size_t length)
{
    // There might be alignment issues, but not for most compilers:
    size_t sz = sizeof(DataPoint) + length * sizeof(ArrayOfThese);
    DataPoint dp = (DataPoint*)calloc( sz );
    // (Check for out of memory)
    dp->a = a; dp->b = b; tp->c = c; dp->length = length;
}

次に、DataPointがその長さを知っているループで「通常」使用できます。

DataPoint *dp = allocDP( 5, 8, 3, 20 );

for(int i=0; i < dp->length; ++i)
{
    // Initialize or access: dp->those[i]
}
于 2009-10-26T20:31:01.207 に答える
1

C++0X より前は、この言語には言及すべきメモリ モデルがありませんでした。そして、新しい標準では、連続性の保証についての話は思い出せません。

この特定の質問に関しては、必要なものがプール アロケータであるかのように聞こえますが、その例は数多く存在します。たとえば、Alexandrescu によるModern C++ Designを考えてみましょう。スモール オブジェクト アロケーターの説明は、注目すべきものです。

于 2009-10-26T19:39:41.547 に答える
1

私はboost::variantこれを達成するかもしれないと思います。私はそれを使用する機会がありませんでしたが、それはユニオンのラッパーであると信じています。そのため、ユニオンの 1std::vectorつが連続している必要がありますが、もちろん、各アイテムは 2 つのサイズのうち大きい方を占めます。サイズの異なる要素を持つベクトル。

boost::variant と boost::anyの比較を見てみましょう。

各要素のオフセットを前の要素の構成に依存させたい場合は、独自のアロケーターとアクセサーを作成する必要があります。

于 2009-10-26T19:43:58.183 に答える
1

配置 new を使用するよりも、ポインターの配列を割り当ててそれを操作する方が簡単なようです。そうすれば、ランタイム コストをほとんどかけずに、配列全体を新しいサイズに再割り当てできます。また、placement new を使用する場合は、デストラクタを明示的に呼び出す必要があります。つまり、1 つの配列に非配置と配置を混在させるのは危険です。何かをする前にhttp://www.parashift.com/c++-faq-lite/dtors.htmlを読んでください。

于 2009-10-26T19:48:12.417 に答える
0

それらを同じスーパークラスを持つクラスにして、スーパークラスをテンプレートとして使用して、選択したお気に入りの stl コンテナーを使用できますか?

于 2009-10-26T19:35:44.470 に答える
0

最終的に書いたコードは次のとおりです。

#include <iostream>
#include <cstdlib>
#include <cassert>
using namespace std;

struct ArrayOfThese {
  int e;
  int f;
};

struct DataPoint {
  int a;
  int b;
  int c;
  int numDPars;
  ArrayOfThese d[0];

  DataPoint(int numDPars) : numDPars(numDPars) {}

  DataPoint* next() {
    return reinterpret_cast<DataPoint*>(reinterpret_cast<char*>(this) + sizeof(DataPoint) + numDPars * sizeof(ArrayOfThese));
  }

  const DataPoint* next() const {
    return reinterpret_cast<const DataPoint*>(reinterpret_cast<const char*>(this) + sizeof(DataPoint) + numDPars * sizeof(ArrayOfThese));
  }
};

int main() {
  const size_t BUF_SIZE = 1024*1024*200;

  char* const buffer = new char[BUF_SIZE];
  char* bufPtr = buffer;

  const int numDataPoints = 1024*1024*2;
  for (int i = 0; i < numDataPoints; ++i) {
    // This wouldn't really be random.
    const int numArrayOfTheses = random() % 10 + 1;

    DataPoint* dp = new(bufPtr) DataPoint(numArrayOfTheses);

    // Here, do some stuff to fill in the fields.
    dp->a = i;

    bufPtr += sizeof(DataPoint) + numArrayOfTheses * sizeof(ArrayOfThese);
  }

  DataPoint* dp = reinterpret_cast<DataPoint*>(buffer);
  for (int i = 0; i < numDataPoints; ++i) {
    assert(dp->a == i);
    dp = dp->next();
  }

  // Here, send it out.

  delete[] buffer;

  return 0;
}
于 2009-10-27T20:01:12.687 に答える
0

2 つの質問:

  1. ArrayOfThese と DataPoint の類似点は本当ですか、それとも投稿の簡略化ですか? つまり、実際の違いは 1 つの int (または同じタイプのアイテムの任意の数) だけですか?
  2. 特定の DataPoint に関連付けられている ArrayOfThese の数はコンパイル時にわかりますか?

前者が当てはまる場合、単純に 1 つの DataPoint+N ArrayOfThese に必要な数の項目の配列を割り当てることについて、私は一生懸命考えます。次に、アイテム N+3 を返すために operator[] をオーバーロードし、最初の 3 つのアイテムを返すために a()、b()、および c() をオーバーロードする簡単なコードを作成します。

2 番目が正しい場合、私は基本的に fnieto が投稿したばかりのものを提案するつもりだったので、これ以上詳しく説明することはしません。

配置 new に関する限り、割り当てについて実際には何も保証されません。実際、配置 new に関する全体的な考え方は、メモリ割り当てとはまったく関係がないということです。代わりに、既に割り当てられているメモリ ブロック内の任意のアドレス (アラインメントの制限に従う) にオブジェクトを作成できます。

于 2009-10-26T19:55:17.253 に答える