11

私は長い間ハードウェア API を扱ってきましたが、これまで取り組んできたほとんどすべての API には C インターフェイスがありました。そのため、多くの場合、ネイキッドnews、安全でないバッファリング、および C++ コードでラップされた多くの C 関数を使用していました。最終的に、C の純粋なコードと C++ の純粋なコードの間のフロンティアは頭の中でめちゃくちゃになりました (そして、このフロンティアを明確にすることがまったく役立つかどうかはわかりません)。

さて、いくつかの新しいコーディング スタイルの要件により、安全でないと思われるすべてのコードを C++ で記述されたより安全なコードにリファクタリングする必要があります (C++ コードの方が安全であると仮定します)。最終的な目標は、 C++ がもたらすツール。

それで、私の混乱をすべて取り除くために、C/C++ のいくつかのトピックについて助けを求めています。

memcpystd::copy

AFAIKmemcpyは C ライブラリにある関数なので、C++ 風ではありません。一方std::copy、STL への関数であるため、純粋な C++ です。

  • しかし、これは本当ですか?結局のところ、データが自明にコピー可能な場合は、(ヘッダーに)std::copy呼び出します。std::memcpycstring
  • memcpyすべての呼び出しを呼び出しにリファクタリングstd::copyすると、コードがより「純粋な C++」になりますか?.

結局、リファクタリングを続けることにした新しいコード スタイルの要件に対処するために、 andmemcpyについていくつか質問がmemcpyありstd::copyます。

memcpy型に関係なくあらゆる種類のポインターを管理できる生の void ポインターで動作しますが、同時に非常に柔軟でstd::copyあり、この柔軟性の欠如により型の安全性が保証されるため、型は安全ではありません。一見したmemcpyところ、シリアライゼーションとデシリアライゼーション ルーチンを使用するための最良の選択です (これが私の実際の使用例です)。たとえば、カスタム シリアル ポート ライブラリを介していくつかの値を送信します。

void send(const std::string &value)
{
    const std::string::size_type Size(value.size());
    const std::string::size_type TotalSize(sizeof(Size) + value.size());
    unsigned char *Buffer = new unsigned char[TotalSize];
    unsigned char *Current = Buffer;

    memcpy(Current, &Size, sizeof(Size));
    Current += sizeof(Size);

    memcpy(Current, value.c_str(), Size);

    sendBuffer(Buffer, TotalSize);

    delete []Buffer;
}

上記のコードは正常に動作しますが、見栄えが悪くなります。std::stringメソッドを介して内部メモリにアクセスするカプセル化を取り除きstd::string::c_str()ます。動的メモリの割り当てと割り当て解除を処理し、ポインターを操作して、すべての値を符号なし文字として扱う必要があります (次の部分を参照)。問題は次のとおりです。これを行うより良い方法はありますか?

を使用して上記の問題を解決しようとする私の最初の試みは、std::copy私を完全に満足させるものではありません。

void send(const std::string &value)
{
    const std::string::size_type Size(value.size());
    const std::string::size_type TotalSize(sizeof(Size) + value.size());

    std::vector<unsigned char> Buffer(TotalSize, 0);

    std::copy(&Size, &Size + 1, Buffer.begin());
    std::copy(value.begin(), value.end(), Buffer.begin() + sizeof(Size));

    sendBuffer(Buffer.data(), TotalSize);
}

上記のアプローチでは、メモリ管理はもはや問題ではありません。std::vectorスコープの最後でデータを割り当て、保存し、最終的に割り当てを解除する責任を負いますが、std::copyポインター演算とイテレーター演算が混在する呼び出しはかなり面倒です。結局、呼び出しのstd::vectorカプセル化を無視しています。sendBuffer

以前の試行の後、s を使用して何かをコーディングしましstd::stringstreamたが、結果はさらに悪くなり、今では次のことを考えています。

  • オブジェクトと値を安全な方法でシリアル化する方法があり、カプセル化を壊すことなく、ポインタ/イテレータ演算を過度または混乱させることなく、動的メモリ管理なしで、またはそれは単に不可能な目標ですか? (はい、 について聞いたことがありますboost::serializationが、今のところ、統合することは許可されていません)。

と:

  • std::copyシリアライゼーション/デシリアライゼーションの目的での最適な使用法は何ですか? (もしあれば)。
  • コンテナまたは配列をコピーするためのstd::copy理論的根拠は限定されており、それを生メモリに使用するのは悪い選択ですか?

alloc/freenew/deletestd::allocator

もう 1 つの大きなトピックは、メモリの割り当てです。私の知る限り、malloc/free関数は C からのものですが、C++ スコープでは禁止されていません。また、new/delete演算子は C++ スコープからのものであり、ANSI C ではありません。

  • 私は正しい?
  • new/ deleteANSI C で使用できますか?

すべての C フレーバー コードを C++ コードにリファクタリングする必要があると仮定すると、いくつかのレガシー コードの周りに広がっているすべてのalloc/を取り除き、free動的メモリの予約が非常に紛らわしいことがわかりました。void 型は何も運びませんタイプとして void を使用してデータ バッファーを予約することは不可能であるため、サイズに関する情報:

void *Buffer = new void[100]; // <-- How many bytes is each 'void'?

pure-raw-binary-data-pointers がないため、へのポインターを作成するのが一般的な方法unsigned charです。要素のchar数とサイズを等しくするため。また、unsignedデータ コピー中の予期しない符号付き - 符号なし変換を回避するためです。多分それは一般的なやり方かもしれませんが、それは混乱です...そうunsigned charではなく、バイナリデータへのある種のダミーポインターを選択することを余儀なくされた場合でも、代わりに好むでしょう。intfloatmy_awesome_serialization_structvoid *unsigned char *

したがって、シリアライゼーション/デシリアライゼーションの目的で動的バッファーが必要な場合unsigned char *、タイプ セキュアなバッファー管理にリファクタリングするためにそれらを回避する方法はありません。allocしかし、すべての/freeペアをnew/ペアに激怒リファクタリングしていたときdeletestd::allocator.

はタイプ セーフな方法でメモリ チャンクを予約できます。一見すると便利だと思いますが、 orstd::allocatorでの割り当てと大きな違いはないと思いました。std::allocator<int>::allocatenew intstd::allocator<int>::deallocatedelete int

そして今、私は動的メモリ管理について北を失ったので、私は尋ねています:

  • タイプセーフな管理を許可するシリアライゼーション/デシリアライゼーションの目的で動的メモリ管理を含む良い C++ プラクティスはありますか?
  • const char *シリアライゼーション/デシリアライゼーション メモリ バッファの使用を避けることは可能ですか?
  • std::allocatorシリアライゼーション/デシリアライゼーションのスコープの根拠とその使用法は何ですか? (もしあれば)。

ご清聴ありがとうございました!

4

1 に答える 1

2

私の経験では、C++ での型の安全性は、コンパイラが型の不一致について文句を言うだけではありません。むしろ、一般に、データのメモリ レイアウトを気にする必要がないことを意味します。実際、C++ 標準には、特定のデータ型のメモリ レイアウトに関する要件がほとんどありません。

あなたのシリアル化は直接メモリ アクセスに基づいているため、単純な「純粋な」C++ ソリューション、特に一般的なコンパイラ/プラットフォームに依存しないソリューションは存在しないのではないかと心配しています。

于 2012-10-15T14:19:42.307 に答える