私は長い間ハードウェア API を扱ってきましたが、これまで取り組んできたほとんどすべての API には C インターフェイスがありました。そのため、多くの場合、ネイキッドnew
s、安全でないバッファリング、および C++ コードでラップされた多くの C 関数を使用していました。最終的に、C の純粋なコードと C++ の純粋なコードの間のフロンティアは頭の中でめちゃくちゃになりました (そして、このフロンティアを明確にすることがまったく役立つかどうかはわかりません)。
さて、いくつかの新しいコーディング スタイルの要件により、安全でないと思われるすべてのコードを C++ で記述されたより安全なコードにリファクタリングする必要があります (C++ コードの方が安全であると仮定します)。最終的な目標は、 C++ がもたらすツール。
それで、私の混乱をすべて取り除くために、C/C++ のいくつかのトピックについて助けを求めています。
memcpy
対std::copy
AFAIKmemcpy
は C ライブラリにある関数なので、C++ 風ではありません。一方std::copy
、STL への関数であるため、純粋な C++ です。
- しかし、これは本当ですか?結局のところ、データが自明にコピー可能な場合は、(ヘッダーに)
std::copy
呼び出します。std::memcpy
cstring
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
/free
対new
/delete
対std::allocator
もう 1 つの大きなトピックは、メモリの割り当てです。私の知る限り、malloc
/free
関数は C からのものですが、C++ スコープでは禁止されていません。また、new
/delete
演算子は C++ スコープからのものであり、ANSI C ではありません。
- 私は正しい?
new
/delete
ANSI 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
ではなく、バイナリデータへのある種のダミーポインターを選択することを余儀なくされた場合でも、代わりに好むでしょう。int
float
my_awesome_serialization_struct
void *
unsigned char *
したがって、シリアライゼーション/デシリアライゼーションの目的で動的バッファーが必要な場合unsigned char *
、タイプ セキュアなバッファー管理にリファクタリングするためにそれらを回避する方法はありません。alloc
しかし、すべての/free
ペアをnew
/ペアに激怒リファクタリングしていたときdelete
、std::allocator
.
はタイプ セーフな方法でメモリ チャンクを予約できます。一見すると便利だと思いますが、 orstd::allocator
での割り当てと大きな違いはないと思いました。std::allocator<int>::allocate
new int
std::allocator<int>::deallocate
delete int
そして今、私は動的メモリ管理について北を失ったので、私は尋ねています:
- タイプセーフな管理を許可するシリアライゼーション/デシリアライゼーションの目的で動的メモリ管理を含む良い C++ プラクティスはありますか?
const char *
シリアライゼーション/デシリアライゼーション メモリ バッファの使用を避けることは可能ですか?std::allocator
シリアライゼーション/デシリアライゼーションのスコープの根拠とその使用法は何ですか? (もしあれば)。
ご清聴ありがとうございました!