6

重要なデータ構造を、プログラム定義の区切り文字を使用して非構造化文字列として格納するプログラムに取り組んでいます(したがって、文字列をウォークして、必要な情報を抽出する必要があります)。これを変換したいと思います。より構造化されたデータ型。

本質的に、これには、構造体に含まれるデータの種類を説明するフィールドを持つ構造体と、データ自体を含む文字列である別のフィールドが必要になります。文字列の長さは、割り当て時に常にわかります。テストを通じて、これらのデータ型のそれぞれに必要な割り当ての数を2倍にすることは、許容できないコストであると判断しました。構造体と構造体に含まれるstd::stringに1回の割り当てでメモリを割り当てる方法はありますか?cstringsを使用している場合は、構造体にchar *を含め、構造体と文字列に十分な大きさのブロックを割り当てた後、構造体の最後を指すようにしますが、可能であればstd::stringを使用することをお勧めします。

私の経験のほとんどはCに関するものなので、ここに表示されているC++の無知を許してください。

4

8 に答える 8

2

あなたがそのような厳しい記憶の必要性を持っているならば、あなたは放棄しなければならないでしょうstd::string

最良の代替策は、 (次のC ++標準ライブラリの提案)の実装を見つけるか作成することです。これは、実際には単なるchar*とサイズの組み合わせです。しかし、それはの(非変異)関数のすべてを持っています。次に、ファクトリ関数を使用して必要なメモリ(構造体サイズ+文字列データ)を割り当て、次に配置newを使用してを初期化します。basic_string_refstd::basic_stringbasic_string_ref

もちろん、「削除」へのポインタを渡すことはできないため、カスタム削除機能も必要になります。


以前に(およびそれに関連するtypedef、)の実装にリンクされbasic_string_refstring_refていた場合、文字列が必要なタイプTのファクトリコンストラクタ/デストラクタは次のとおりです。

template<typename T> T *Create(..., const char *theString, size_t lenstr)
{
  char *memory = new char[sizeof(T) + lenstr + 1];
  memcpy(memory + sizeof(T), theString, lenstr);

  try
  {
    return new(memory) T(..., string_ref(theString, lenstr);
  }
  catch(...)
  {
    delete[] memory;
    throw;
  }
}

template<typename T> T *Create(..., const std::string & theString)
{
  return Create(..., theString.c_str(), theString.length());
}

template<typename T> T *Create(..., const string_ref &theString)
{
  return Create(..., theString.data(), theString.length());
}

template<typename T> void Destroy(T *pValue)
{
  pValue->~T();

  char *memory = reinterpret_cast<char*>(pValue);
  delete[] memory;
}

もちろん、他のコンストラクターパラメーターを自分で入力する必要があります。また、型のコンストラクターは、文字列を参照するを取得する必要がありstring_refます。

于 2012-06-08T13:15:52.913 に答える
1

これがあなたの問題に正確に対処しているかどうかはわかりません。事前に割り当てられたバッファを使用してから「placementnew」演算子を使用することにより、C++でのメモリ割り当てを最適化できる1つの方法。私はそれを理解したのであなたの問題を解決しようとしました。

 unsigned char *myPool = new unsigned char[10000];
 struct myStruct
 {
    myStruct(char* aSource1, char* aSource2)
    {
        original = new (myPool) string(aSource1); //placement new
        data = new (myPool) string(aSource2); //placement new
    }
    ~myStruct()
    {
        original = NULL; //no deallocation needed
        data = NULL; //no deallocation needed
    }
    string* original;
    string* data;
};

int main()
{
    myStruct* aStruct = new (myPool) myStruct("h1", "h2");

    //  Use the struct

    aStruct = NULL; //  No need to deallocate
    delete [] myPool;

    return 0;
}

[編集]NicolBolasからのコメントの後、問題はもう少し明確になりました。実際には、生の文字配列を使用するよりもそれほど有利ではありませんが、もう1つ答えを書くことにしました。しかし、私はまだこれが述べられた制約の範囲内であると信じています。このSOの質問で指定されているように、文字列クラスにカスタムアロケーションを提供することをお勧めします。割り当てメソッドの実装では、新しい配置を次のように使用します

pointer allocate(size_type n, void * = 0) 
{
    // fail if we try to allocate too much
    if((n * sizeof(T))> max_size()) { throw std::bad_alloc(); }

    //T* t = static_cast<T *>(::operator new(n * sizeof(T)));
    T* t = new (/* provide the address of the original character buffer*/) T[n];
    return t;
}

制約は、新しい配置が機能するためには、実行時に元の文字列アドレスが割り当て者に認識されている必要があるということです。これは、新しい文字列メンバーを作成する前に、外部の明示的な設定によって実現できます。ただし、これはそれほどエレガントではありません。

于 2012-06-08T12:59:46.000 に答える
1

を使用している場合std::string、構造と文字列の両方に1つの割り当てを実際に行うことはできません。また、両方の割り当てを1つの大きなブロックにすることもできません。ただし、古いCスタイルの文字列を使用している場合は可能です。

于 2012-06-08T12:18:25.990 に答える
1

私があなたを正しく理解しているなら、あなたはプロファイリングを通して、あなたがstringあなたのデータ構造にと別のデータメンバーを割り当てなければならないという事実があなたのアプリケーションに容認できないコストを課すとあなたが決定したと言っています。

それが実際に当てはまる場合、私はいくつかの解決策を考えることができます。

  1. プログラムを開始する前に、これらすべての構造を事前に割り当てることができます。それらがコピー構築されないように、ある種の固定コレクションに保持し、データを保持するのにreserve十分なバッファstringを保持します。
  2. 物議をかもしているように思われるかもしれませんが、古いCスタイルのchar配列を使用できます。stringそもそもメモリ管理であるsを使用する理由の多くに焦点を当てているようです。ただし、あなたの場合、起動時に必要なバッファサイズがわかっているので、これを自分で処理できます。提供する他の機能が気に入った場合stringは、その多くがまだ<algorithm>sで利用可能であることを覚えておいてください。
于 2012-06-08T12:37:03.473 に答える
1

Variable Sized Struct C ++を見てください-簡単な答えは、バニラC++ではそれを行う方法がないということです。

本当にヒープにコンテナ構造体を割り当てる必要がありますか?それらをスタックに置く方が効率的かもしれないので、それらを割り当てる必要はまったくありません。

于 2012-06-08T12:44:23.790 に答える
1

Cスタイルの文字列は、必要に応じていつでも変換できstd::stringます。実際、プロファイリングからの観察結果は、単に割り当ての数ではなく、データの断片化によるものである可能性が高く、std::stringオンデマンドでの作成が効率的です。もちろん、実際のアプリケーションがわからない場合、これは単なる推測であり、とにかくテストされるまで、実際にはこれを知ることはできません。クラスを想像します

class my_class {
    std::string data() const { return self._data; }
    const char* data_as_c_str() const // In case you really need it!
    { return self._data; }
private:
    int _type;
    char _data[1];
};

データレイアウトに標準の巧妙なCトリックを使用したことに注意_dataしてください。ファクトリ関数が余分なスペースを割り当てている限り、必要な長さです。IIRC、C99はそれのために特別な構文さえ与えました:

struct my_struct {
    int type;
    char data[];
};

これは、C++コンパイラで動作する可能性が高いです。(これはC ++ 11標準ですか?)

もちろん、これを行う場合は、すべてのコンストラクターをプライベートにしてファクトリ関数と友達にする必要があります。これにより、ファクトリ関数が実際にインスタンス化する唯一の方法にmy_classなります。配列。必ずoperator=プライベートにするか、慎重に実装する必要があります。


データ型を再考することはおそらく良い考えです。

たとえば、実行できることの1つは、char配列を構造化データ型にしようとするのではなく、代わりにスマート参照を使用することです。次のようなクラス

class structured_data_reference {
public:
    structured_data_reference(const char *data):_data(data) {}
    std::string get_first_field() const {
        // Do something interesting with _data to get the first field
    }
private:
    const char *_data;
};

他のコンストラクターや代入演算子でも正しいことをしたいと思うでしょう(おそらく代入を無効にし、移動とコピーに適したものを実装します)。std::shared_ptrまた、裸のポインターではなく、コード全体で参照カウントされたポインター(例)が必要な場合があります。


可能な別のハックは、を使用することですstd::stringが、タイプ情報を最初のエントリ(または最初のいくつか)に格納します。もちろん、これには、データにアクセスするたびにそれを考慮する必要があります。

于 2012-06-08T13:18:23.610 に答える
1

実際、2つの割り当ては高すぎるように見える場合があります。ただし、それらを削減するには2つの方法があります。

  • 単一の割り当てを行う
  • 単一の動的割り当てを行う

それほど変わらないように見えるかもしれませんので、説明させてください。

struct1.C++でハックを使用できます

  • はい、これは典型的なC++ではありません
  • はい、これには特別な注意が必要です

技術的には以下が必要です。

  • コピーコンストラクタと代入演算子を無効にする
  • コンストラクタとデストラクタprivateを作成し、オブジェクトの割り当てと割り当て解除のためのファクトリメソッドを提供します

正直なところ、これは難しい方法です。

2.アウターstructを動的に割り当てることを避けることができます

十分に単純:

struct M {
    Kind _kind;
    std::string _data;
};

次に、のインスタンスをMスタックに渡します。移動操作は、std::stringがコピーされないことを保証する必要があります(コピーを確実に無効にすることができます)。

このソリューションははるかに簡単です。唯一の(わずかな)欠点はメモリの局所性にあります...しかし一方で、スタックの最上位はとにかくすでにCPUキャッシュにあります。

于 2012-06-08T13:21:44.200 に答える
-1

本質的に、これには、構造体に含まれるデータの種類を説明するフィールドを持つ構造体と、データ自体を含む文字列である別のフィールドが必要になります。

ここでは、C++の型システムを最大限に活用していないのではないかと思います。見た目も感じもとてもCっぽいです(それは適切な言葉ではありません、私は知っています)。あなたが解決しようとしている問題について私にはわからないので、ここに投稿する具体的な例はありません。

構造体と構造体に含まれるstd::stringに1回の割り当てでメモリを割り当てる方法はありますか?

構造体の割り当てとそれに続く構造体メンバーへの文字列のコピーについて心配していると思いますか?これは理想的には発生しないはずです(ただし、もちろん、これはメンバーを初期化する方法とタイミングによって異なります)。C++11は移動構築をサポートします。これにより、心配している余分な文字列のコピーを処理する必要があります。

この議論を価値のあるものにするために、本当に、本当にいくつかのコードを投稿する必要があります:)

プログラム定義の区切り文字を使用した非構造化文字列としての重要なデータ構造

1つの質問:この文字列は変更可能ですか?そうでない場合は、わずかに異なるデータ構造を使用できます。この重要なデータ構造の一部のコピーを保存するのではなく、区切り文字を指すこの文字列へのインデックス/イテレータを保存します。

 // assume that !, [, ], $, % etc. are your program defined delims
 const std::string vital = "!id[thisisdata]$[moredata]%[controlblock]%";

 // define a special struct
 enum Type { ... }; 
 struct Info {
     size_t start, end;
     Type type;
     // define appropriate ctors
 };

 // parse the string and return Info obejcts
 std::vector<Info> parse(const std::string& str) {
      std::vector<Info> v;
      // loop through the string looking for delims
      for (size_t b = 0, e = str.size(); b < e; ++b) {
            // on hitting one such delim create an Info
            switch( str[ b ] ) {
                case '%':
                  ... 
                case '$;:    
                // initializing the start and then move until
                // you get the appropriate end delim
            }
            // use push_back/emplace_back to insert this newly
            // created Info object back in the vector
            v.push_back( Info( start, end, kind ) );
      }
      return v;
 }
于 2012-06-08T12:20:59.537 に答える