関数のオーバーロードのためだけにC ++コンパイラを使用するのは悪い習慣ですか?
私見の観点からはい、両方の言語が大好きなので、これに答えるために統合失調症になる必要がありますが、それは効率とは関係ありませんが、言語の安全性と慣用的な使用に似ています。
C側
Cの観点からすると、関数のオーバーロードを使用するためだけにコードにC ++を必要とするのは非常に無駄です。C ++テンプレートを使用した静的なポリモーフィズムに利用しない限り、まったく異なる言語への切り替えと引き換えに得られるこのような取るに足らない構文上の砂糖です。さらに、関数をdylibにエクスポートしたい場合(実際の懸念かもしれませんが、そうでない場合もあります)、すべての名前がマングルされたシンボルを使用して広範囲に消費することは、もはや実際にはできません。
C ++側
C ++の観点からは、関数のオーバーロードを伴うCのようなC ++を使用しないでください。これは文体的な教義ではなく、日常のC ++の実用化に関連したものです。
あなたの通常の種類のCコードは、でコピーctorのようなものを禁止しているC型システムに対して作業している場合にのみ、合理的に健全で「安全に」書くことができますstructs
。あなたは次のように莫大な価値があるC ++の豊かな多くのタイプのシステム、日常の機能で作業したらmemset
とmemcpy
あなたがすべての時間に傾くべき機能にはなりません。代わりに、それらはペストのように一般的に避けたい関数です。C++型では、コピーしてシャッフルして解放する生のビットやバイトのように扱うべきではないからです。コードmemset
が現時点でプリミティブやPOD UDTのようなものだけを使用している場合でも、使用するUDTに誰かがctorを追加する瞬間(たとえば、std::unique_ptr
メンバー)そのような関数または仮想関数またはそのようなものに対して、未定義の動作の影響を受けやすい通常のCスタイルのコーディングをすべてレンダリングします。ハーブサッター自身からそれを取ります:
memcpy
とmemcmp
型システムに違反します。memcpy
オブジェクトのコピーに使用することは、コピー機を使用してお金を稼ぐようなものです。memcmp
オブジェクトの比較に使用することは、ヒョウのスポットを数えてヒョウを比較するようなものです。ツールと方法は仕事をしているように見えるかもしれませんが、それらは容認できるようにするには粗すぎます。C ++オブジェクトはすべて情報隠蔽に関するものです(ソフトウェアエンジニアリングでおそらく最も収益性の高い原則、項目11を参照):オブジェクトはデータを隠し(項目41を参照)、コンストラクターと割り当て演算子を介してそのデータをコピーするための正確な抽象化を考案します(項目52から55を参照) 。それをすべてブルドージングするとmemcpy
、情報隠蔽の重大な違反となり、多くの場合、メモリとリソースのリーク(せいぜい)、クラッシュ(悪化)、または未定義の動作(最悪)につながります-C ++コーディング標準。
哲学はC ++でコードを記述している場合にのみ当てはまるため、多くのC開発者はこれに異議を唱えます。あなたは、最も可能性が高いしているあなたのような機能を使用する場合に非常に問題のあるコードを書くmemcpy
すべての時間C ++のように構築したコードでは、しかし、あなたはそれを行う場合、それは完全に罰金ですCで。2つの言語は、型システムの違いにより、この点で大きく異なります。これら2つの共通の機能のサブセットを見て、特にC ++側で一方が他方のように使用できると信じているのは非常に魅力的ですが、C +コード(またはC--コード)は一般にCとC ++コード。
同様に、たとえば、malloc
スローできるC ++関数を直接呼び出すことができる場合、Cスタイルコンテキスト(EHを意味しない)で使用しないでください。これは、関数の結果として暗黙的な終了点があるためです。free
そのメモリに到達する前にCスタイルのコードを書くのを効果的にキャッチできない例外。ですから、C ++のように構築し、ファイル持っている時はいつでも.cpp
拡張子または何、それはのようなもののすべてのこれらのタイプを行いますがmalloc
、memcpy
、memset
、qsort
など、その後、まだプリミティブ型でのみ動作するクラスの実装の詳細でない限り、さらに下流の問題を求めていますが、その時点で例外処理を行う必要があります。あなたがC ++のコードを書いている場合は、代わりに、一般的にRAIIに依存しているとのようなものを使用したいvector
、unique_ptr
、shared_ptr
、などを、そして可能な場合、すべての通常のCスタイルのコーディングを避けます。
CおよびX線データタイプのカミソリの刃で遊ぶことができ、チームで副次的な損傷を引き起こす傾向がなく、そのビットとバイトで遊ぶことができる理由は(あなたはまだどちらにしても本当に自分を傷つけることができますが)型はできますが、それらができないことのために。Cの型システムを拡張して、ctor、dtor、vtablesなどのC ++機能と例外処理を含めると、すべての慣用的なCコードが現在よりはるかに危険になり、新しい種類のC ++で見られるように、まったく異なるスタイルのコーディングを奨励する哲学と考え方が進化します。C++では、たとえば、RAII準拠のリソースとは対照的に、メモリを管理するクラスに生のポインター不正を使用することも検討していますunique_ptr
。その考え方は、絶対的な安全感覚から発展したものではありません。型システムで許可されているだけの例外処理のような機能に対してC ++が特に安全である必要があるものから進化しました。
例外安全性
繰り返しになりますが、あなたがC ++の土地にいる瞬間、人々はあなたのコードが例外安全であることを期待するでしょう。コードはすでにC ++で記述およびコンパイルされておりstd::vector, dynamic_cast, unique_ptr, shared_ptr
、コードによって直接または間接的に呼び出されるコードでを使用するなど、コードはすでに「想定」されているため、無害であると信じて、将来あなたのコードを維持する可能性がありますコード。その時点で、物事がスローされる可能性に直面する必要があります。次に、次のような完全に素晴らしく素敵なCコードを取得します。
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future.
...
free(x);
return success;
}
return fail;
}
...壊れています。例外に対して安全になるように書き換える必要があります。
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
try
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future (maybe someone used
// std::vector inside of it).
}
catch (...)
{
free(x);
throw;
}
...
free(x);
return success;
}
return fail;
}
キモい!これが、ほとんどのC ++開発者が代わりにこれを要求する理由です。
void some_func(int n, ...)
{
vector<int> x(n);
f(x); // some function which, now being a C++ function, may throw today
// or in the future.
}
上記は、C ++開発者が一般に承認する種類のRAII準拠の例外セーフコードです。これは、関数がの結果として暗黙的な終了をトリガーするコード行をリークしないためですthrow
。
言語を選択してください
RAII、例外安全性、テンプレート、OOPなどでC ++の型システムと哲学を採用するか、生のビットとバイトを中心に展開するCを採用する必要があります。これらの2つの言語の間で不浄な結婚を形成するべきではなく、それらを別々の言語に分けて、それらを一緒に曖昧にするのではなく、非常に異なって扱うべきではありません。
これらの言語はあなたと結婚したいです。あなたは通常、両方でデートして浮気するのではなく、どちらかを選択する必要があります。または、あなたは私のような一夫多妻主義者であり、両方と結婚することができますが、お互いに時間を過ごすときは完全に思考を切り替えて、お互いに戦わないようにお互いを十分に離しておく必要があります。
バイナリサイズ
好奇心から、今すぐフリーリストの実装とベンチマークを試し、C ++に移植してみました。
[...] Cコンパイラを使用していないので、Cでどのように見えるかわかりません。
...そして、バイナリサイズがC ++としてビルドするだけで膨張するかどうかを知りたいと思っていました。ひどい場所に明示的なキャストを振りかける必要がありました(アロケーターやCでのデータ構造のような低レベルのものを実際に書くのが好きな理由の1つ)が1分しかかかりませんでした。
これは単純なコンソールアプリケーションのためのMSVC 64ビットのリリースビルドを比較し、いずれかのC ++の機能を使用していないコードで、演算子のオーバーロードしてもいませんでした- Cでそれを構築し、使用して、たとえば、間単に違い<cstdlib>
の代わり<stdlib.h>
とそのようなことですが、バイナリサイズとの差がゼロになったことに驚きました!
バイナリは9,728
、Cでビルドされた場合はバイトであり9,278
、C ++コードとしてコンパイルされた場合もバイトでした。私は実際にそれを期待していませんでした。EHのようなものは、少なくとも少しは追加されると考えました(少なくとも100バイトは異なると考えられていました)が、たぶん、 C標準ライブラリを使用すると、何もスローされません。私は何かを考えましたRTTIのように、どちらの方法でもバイナリサイズに少し追加されます。とにかく、それを見るのはちょっとクールでした。もちろん、この1つの結果から一般化する必要はないと思いますが、少なくとも少し感銘を受けました。また、ベンチマークに影響を与えることはありませんでした。当然のことながら、同じ結果のバイナリサイズは、結果のマシン命令も同じになると思います。
とはいえ、上記の安全性とエンジニアリングの問題でバイナリサイズを気にする人はいますか?繰り返しになりますが、言語を選択して、その哲学を受け入れようとするのではなく、その哲学を受け入れます。それがお勧めです。
//
コメントができるように、C機能のみを使用するプロジェクトにC ++を使用することが知られています。それが機能する場合、どうしてですか?