2

const u_char *を使用した生データへのポインターと、このようなジェネリッククラスがあります

class Rectangle 
{
   u_int8_t length;
   u_int8_t height;
   ...
}

生データがバイトのバイナリ「ストリーム」であると仮定すると、生データをクラスのフィールドに取り込むための最良の方法は何ですか。

-memcpy ?
-cast   ?

私はこれを行うことができます:

Rectangle *rect = (Rectangle*)rawdata;

しかし、私はそれが「古いスタイル」のキャストであることを知っています。

適切な方法は何ですか?

4

5 に答える 5

2

コンストラクターがあると思います:

Rectangle(const u_char*)

コード全体にキャストすることは今のところうまくいくかもしれませんが、後でクラスを変更したい場合に備えて、それはひどい考えです。コンストラクターがあると、いくらかのオーバーヘッドが発生する可能性がありますが、ロジックが発生する単一のポイントがあります。

後で仮想メソッドをに追加することにした場合Rectangle、コード内のすべてのキャストは役に立たなくなります。

もちろん、これはデータから新しいオブジェクトを作成する場合です。オブジェクトを頻繁にシリアル化/逆シリアル化する場合は、シリアル化メソッドを使用します。

const u_char* toUChar() const;
void fromUChar(const u_char*) const;
于 2012-10-08T19:40:28.357 に答える
2
Rectangle& rect = *reinterpret_cast<Rectangle*>(rawdata);

rawdataisの場合はこの方法で実行し、が他のタイプ(直接参照できるタイプ)のvoid場合は参照に直接キャストします。rawdata

生のポインターを使用するよりもエラーが発生しにくいので、参照を好みます。ただし、ポインタ演算を行う必要がある場合は、生のポインタにするのに問題はありません。const Rectangle&使用法によっては、非の代わりにキャストしたい場合がありますconst

ただし、通常、生のバイトストリームでは、プロトコルを考案する必要があり、構造体やクラスに直接キャストしないでください。構造体とクラスには、直接キャストを台無しにするパディングが含まれている場合があります。キャストは何があっても静かに成功しますが、あなたの価値観は予想外です。プロトコルは次のようになります...

0オフセット:(4バイト-フロート)サイズ

4オフセット:(2バイト-uint16_t)高さ

プロトコルアプローチを実行すると、メンバーを1つずつ割り当てる必要があります。

于 2012-10-08T19:44:50.907 に答える
1

最も簡単で信頼性の高い方法は、単純に変換コンストラクターを使用することです。

class Rectangle
{
public:
  Rectangle(uint8_t l, uint8_t h) : length(l), height(h) {};
  // ...
};

何らかの理由でこれが不可能になるまで、これはあなたの頼りになる方法でなければなりません。

これを除けば、次に行うべき最善のことは、単にメンバーごとの初期化を行うことです。

Rectangle rect;
rect.width = 20;
rect.height = 40;

上記を行うことが不可能になり、問題のオブジェクトが標準で「集約」(基本的にはPOD)と呼ばれるものである場合は、次のような初期化子を使用できます。

Recatagle rect = {10,20};

これを行うときは、メンバーがクラスで宣言された順序で初期化されることに注意する必要があります。宣言の順序を変更すると、上記のようにすべての初期化が中断されます。これは非常に壊れやすいです。このため、このような構造の使用は、問題のクラスが高度にローカライズされている場合(単一の翻訳ユニットのヘルパークラスなど)に限定し、宣言の順序をそのまま維持する必要があることを文書化します。

コメントごとに編集:

文字列をクラスにコピーしようとしている場合、または任意の種類のデータへのポインタをコピーしようとしている場合は、ディープコピーを実行する必要があります。

class Gizmo
{
public:
  Gizmo(const char* str) : str_(0)
  {
    str_ = new char[strlen(str)+1];
    strcpy(str_,str);
  }
};

不器用さと上記のコードがいかに脆弱であるかに注意してください。ここでうまくいかないことがたくさんあります。delete str_何よりも、いつ破壊されるかを忘れていることGizmo、醜いこと、そもそも文字列を作成newする必要性がないこと、過去1回のエラー...リストは続きます。charこれらの理由から、生のポインターを使用したり、スマートポインター(つまりunique_ptrshared_ptrなど)またはコレクションクラスを使用したりすることは絶対に避けてください。この場合、std::stringコレクションクラスと考えることができるを使用します。

class Gizmo
{
public:
  Gizmo(const char* str) : str_(str) {};
private:
  std::string str_;
};

これを自由に変換して、で使用できるようにしu_char*、ソースポインタが有効であることを確認することで堅牢性を追加してください。

于 2012-10-08T19:46:08.083 に答える
0

「新しいスタイル」のキャストは

Rectangle *rect = reinterpret_cast< Rectangle* >( rawdata );

ただし、クラスの配置、パディングには注意する必要があります。

これは同じメモリを使用し、Rectanleオブジェクトのように解釈します。

memcpyそれをコピーします。ニーズによって異なります。おそらく、あなたはそれを必要としないでしょう。

于 2012-10-08T19:37:11.107 に答える
0

直接キャストする方が少し高速ですが、そうすることでクラスを特定のデザインに固定できます。後でクラス内のデータに変更を加えると、すべてのキャストが中断されます。

生データを処理するためのより良い方法は、コンストラクターを作成するか、入力ストリーム演算子をオーバーロードすることです。私は個人的に、クラスに複数の単純なコンストラクターを持つのが嫌いなので、コンストラクターよりも演算子をオーバーロードすることを好みます。オーバーロードは次のようになります。

istream& operator >> (istream& input, char* rawData)
{
    //fill your object's variables directly from the raw data
}

このようにして、データからオブジェクトへの入力を制御できます。データまたはオブジェクトが変更された場合でも、1つの場所でコードを変更するだけで、どこでも修正できます。

また、関数を作成するよりも演算子のオーバーロードを使用するのが好きです。これにより、コードの見栄えが良くなり、実行内容をすばやく確認できるようになると思います。

于 2012-10-08T19:50:04.987 に答える