CとC ++のコードを混在させるべきではないと思います。これは問題ですか、どのように修正しますか?


10

背景/シナリオ

純粋にCでCLIアプリケーションの作成を開始しました(「Hello World」またはそのバリエーションではない、最初の適切なCまたはC ++プログラム)。途中でユーザー入力(文字配列)の「文字列」を操作していて、C ++文字列ストリーマーオブジェクトを発見しました。これらを使用してコードを保存できることがわかったので、アプリケーションで使用しました。これは、ファイル拡張子を.cppに変更し、g++ではなくでアプリをコンパイルしたことを意味しますgcc。これに基づいて、私はアプリケーションが技術的にC ++アプリケーションであると言います(ただし、コードの90%以上はCと呼ばれるもので記述されていますが、二つ)。これは、約900行の単一の.cppファイルです。

重要な要素

私はプログラムを無料で(お金のように)自由に配布し、すべての人が使用できるようにしたいと考えています。私の懸念は、誰かがコードを見て、以下の影響について何かを考えることです:

ああコーディングを見て、それはひどいです、このプログラムは私を助けることができません

潜在的にそれができるとき!もう1つの問題は、コードが効率的であることです(イーサネット接続をテストするためのプログラムです)。アプリケーションやその出力のパフォーマンスを著しく妨げるほど非効率なコードの部分があってはなりません。ただし、特定の関数、メソッド、オブジェクト呼び出しなどのヘルプを求めるときのスタックオーバーフローの問題だと思います。

私の質問

(私の意見では)CとC ++を混在させるべきではないのですが。それをすべてC ++で書き直そうとする必要がありますか(これにより、新しいC ++テクニックを使用して圧縮できるCスタイルで何かをコーディングした可能性があるC ++オブジェクトとメソッドを実装することを意味します)、または文字列ストリーマーオブジェクトとすべてをCコードに「戻し」ますか?ここに正しいアプローチはありますか?私は迷っていて、このアプリケーションを大衆の目に「良い」状態に保つ方法についてのガイダンスが必要です。大衆の目には、それを使用して利益を得るでしょう。

コード-更新

ここにコードへのリンクがあります。約40%のコメントです。流暢になるまで、ほぼすべての行にコメントします。ただし、リンクしたコピーでは、ほとんどすべてのコメントが削除されています。これで読みづらくならないことを願っています。誰もがそれを完全に理解する必要がないことを望んでいます。致命的な設計上の欠陥を作った場合でも、それらを簡単に識別できることを願っています。また、Ubuntuデスクトップとラップトップをいくつか書いています。コードを他のオペレーティングシステムに移植するつもりはありません。


3
CLIを明確にしました。CLIは、C言語の観点からはあまり意味がないCommon Language Infrastructureを指すこともあります。
Robert Harvey

FORTRANで書き直すことができます。OOFについては聞いたことがありません。
ott-- 2013年

2
それが「きれい」でなければ、私はあなたのソフトウェアを使わない人々についてあまり心配しません。ユーザーの99%は、必要なことを達成している限り、コードを見たり、コードの記述方法を気にしたりしません。コードを支援するなど、一貫していることが重要であるあなたがそれを長期的に維持することに。
Evicatos 2013年

1
githubなどのコードを使用して、無料ソフトウェアGPLv3ライセンスの下など)を作成してみませんか。コードにファイルがありません。あなたは興味深いフィードバックを得るかもしれません。LICENSE
バジルスタリンケビッチ2015年

回答:


13

最初から始めましょう。CとC ++の混合コードはかなり一般的です。それであなたは最初に大きなクラブにいます。巨大なCコードベースが公開されています。しかし、明らかな理由により、多くのプログラマーは同じコンパイラーでC ++にアクセスできるため、Cで少なくとも新しいものを書くことを拒否し、新しいモジュールはそのように書かれ始めます-最初は既存の部分をそのままにしておきます。

その後、最終的にいくつかの既存のファイルがC ++として再コンパイルされ、いくつかのブリッジを削除できます...しかし、それは本当に長い時間がかかる場合があります。

あなたはいくらか先を行っており、完全なシステムはC ++になり、そのほとんどが「Cスタイル」で書かれています。そして、スタイルの混合が問題であることがわかります。C++は多くのスタイルをサポートするマルチパラダイム言語であり、それらを共存させることができます。実際、それが主な強みであり、単一のスタイルに強制されることはありません。あちこちで最適とは言えない運があり、どこかで運が悪かった。

コードベースが壊れている場合は、コードベースを作り直すことをお勧めします。あるいは、それが開発の邪魔になる場合。しかし、それが機能する場合は(本来の意味で)、最も基本的なエンジニアリングの原則に従ってください。壊れていない場合は、修正しないでください。冷たい部分をそのままにして、それが重要な場所にあなたの努力を置きます。悪い、危険な部品、または新機能の部品について、そして部品をリファクタリングしてベッドにします。

対処する一般的なことを求める場合は、Cコードベースから削除する価値があるものを以下に示します。

  • すべてのstr *関数とchar []-文字列クラスに置き換えます
  • sprintfを使用する場合は、結果を文字列で返すバージョンを作成するか、文字列に配置して、使用法を置き換えます。(ストリームを気にしたことがない場合は、気に入らない限り、自分自身に賛成してスキップしてください。gccは、フォーマットをチェックするための箱から出して完璧なタイプセーフを提供します。適切な属性を追加するだけです。
  • ほとんどのmallocおよびfree-新規および削除ではなく、ベクター、リスト、マップ、およびその他のコレクション。
  • 残りのメモリ管理(前の2つのポイントの後は非常にまれである必要があります。スマートポインタでカバーするか、特別なコレクションを実装してください)
  • 他のすべてのリソース使用法(FILE *、mutex、lockなど)を置き換えて、RAIIラッパーまたはクラスを使用する

それが終わったら、コードベースが合理的に例外セーフになることができるポイントに近づくので、例外を使用してリターンコードのフットボールをドロップし、高レベルの関数でのみ珍しい try / catchを実行できます。

それ以上に、健全なC ++で新しいコードを書くだけでなく、既存のコードに置き換わるクラスが生まれた場合は、それらを選択してください。

私は構文関連のものについては触れませんでした。明らかに、すべての新しいコードでポインターの代わりにrefsを使用しますが、その変更のためだけに古いCパーツを置き換えることは良い値ではありません。対処する必要があるキャスト、できる限りの排除、および残りのラッパー関数でのC ++バリアントの使用。そして非常に重要なことに、該当する場合は常にconstを追加します。これらは以前の弾丸とインターリーブします。マクロを統合し、列挙型、インライン関数、またはテンプレートにできるものを置き換えます。

Sutter / AlexandrescuのC ++ Coding Standardsをまだ読んでいない場合は読んで、それらをよく理解することをお勧めします。


入力バログ、私の目のすべての健全なアドバイスに感謝し、私はそれのすべてに同意します。作業中のコードを優先して、コードをセクションごとにゆっくりと変更していきます。
jwbensley 2013年

7

要するに、あなたは地獄に行くつもりはありません、何も悪いことは起こりませんが、あなたはどんな美の競争にも勝つことはありません。

C ++を「より優れたC」として使用することは完全に可能ですが、C ++の多くの利点を捨てることになりますが、バニラCに限定されないため、Cの利点(単純性、移植性)は得られません。 、透明性、相互運用性)のいずれか。言い換えると、C ++はCの一部の品質を犠牲にして他のものを獲得します。たとえば、メモリ割り当てがいつどこでいつ発生するかを常に明確に確認できるCの透過性は、C ++のより強力な抽象化と交換されます。

あなたのコードは今は大丈夫だと思われるので、それだけのためにそれを書き直すのはおそらく良い考えではありません:今のところそのままにして、あなたが行くごとにもっと慣用的なC ++に一度に変更してください、あなたが与えられた部分に取り組むときはいつでも。そして、次のプロジェクト、特にCとC ++は同じ言語ではなく、プロジェクトの途中でC ++に切り替えることを決定するよりも、前もって選択する方が得策です。 。


tdammersにアドバイスをありがとう。私はあなたが言っていることに同意し、私はそれを船上に持ち込んでいます、ありがとう!
jwbensley 2013年

4

C ++は、多くの機能を備えているという意味で、非常に複雑な言語です。また、複数のパラダイムをサポートする言語でもあります。つまり、オブジェクトを使用せずに完全に手続き型のコードを記述できます。

したがって、C ++は非常に複雑であるため、コードで機能の一部の限られたサブセットを使用していることがよくあります。初心者は多くの場合、malloc / freeの代わりにストリームI / O、文字列オブジェクト、およびnew / deleteを使用し、ポインターの代わりに参照を使用するだけです。オブジェクト指向の機能について学ぶと、「C with classes」と呼ばれるスタイルで書き始めることができます。最終的には、C ++についてさらに学習するにつれて、テンプレート、RAIISTL、スマートポインターなどの使用を開始します。

私が言いたいのは、C ++の学習は時間がかかるプロセスだということです。はい、現在、あなたのコードはおそらくC ++を書こうとしているCプログラマーによって書かれたように見えます。そして、あなたはただ学んでいるだけなので、それはまったく問題ありません。しかし、熟知した熟練したプログラマーが書いたプロダクションコードでこの種のことを見ると、私はうんざりします。

ただ覚えておいてください。優れたFortranプログラマーは、あらゆる言語で優れたFortranコードを記述できます。:)


2

C ++に行くときに多くの古いCプログラマーがたどった経路を正確に要約しているようです。あなたはそれを「より良いC」として使用しています。これは20年前にはある程度の意味がありましたが、現時点では(標準テンプレートライブラリのように)C ++には非常に強力な構成要素が非常に多いため、今ではほとんど意味がありません。コードを見たときにC ++プログラマーに動脈瘤を与えないようにするために、少なくとも次のことを行う必要があります。

  • ?の代わりにストリームを使用します。printf家族。
  • new代わりに使用しますmalloc
  • すべてのデータ構造にSTLコンテナクラスを使用します。
  • 可能な限りstd::string代わりに使用char*
  • RAIIを理解して使用する

プログラムが短い場合(および約900行が短いように見える場合)、私は個人的には、堅牢なクラスのセットを作成しようとすることが必要だとは考えておらず、役に立たないとさえ思っています。


Stevenさん、アドバイスをありがとうございます。そうです、クラスについても同じ考えがあり、コードを1つのきちんとした1つのファイルにまとめることができると思います。ありがとう!
jwbensley 2013年

2

受け入れられた回答は、C ++コードがCコードよりも絶対的な意味で優れているかのように、Cを慣用的なC ++に変換する長所のみを述べています。混合されたコードが壊れていなければ、大幅な変更を行う必要はおそらくないという他の回答にも同意します。

CとC ++の混合が持続可能かどうかは、混合がどのように行われるかに依存します。たとえば、Cコードで例外が発生した場合(これは例外セーフではありません)、微妙なバグが発生し、メモリリークやデータ破損が発生する可能性があります。より安全で一般的な方法は、Cライブラリまたはインターフェイスをクラスでラップするか、C ++プロジェクトで他の種類の分離でそれらを使用することです。

プロジェクト全体を慣用的なC ++(またはC)に書き直すことを決定する前に、他の回答で示された変更の多くがプログラムを遅くしたり、他の望ましくない影響を引き起こす可能性があることに注意する必要があります。例えば:

  • スタックに割り当てられたC文字列をstd :: stringsに変更すると、不要なヒープ割り当てが発生する可能性があります
  • 生のポインターをいくつかの共有ポインター型(std :: shared_ptrなど)に変更すると、参照カウント、内部仮想メンバー関数、およびスレッドセーフによるアクセスオーバーヘッドが発生する
  • 標準ライブラリストリームは、Cライブラリよりも遅い
  • コンテナーなどのRAIIクラスを不注意に使用すると、特にC ++ 11の移動セマンティクスを使用できない場合に、不要な操作が発生する可能性があります
  • テンプレートは、コンパイル時間の増加、不明確なコンパイルエラー、コードの膨張、コンパイラ間の移植性の問題を引き起こす可能性があります
  • 例外セーフなコードを書くことが難しいため、変換中に微妙なバグを簡単に導入できます
  • 普通のCよりも共有ライブラリのC ++コードを使用する方がトリッキーです
  • C ++はC89ほど広くサポートされていません

結論として、CとC ++の混合コードから慣用的なC ++への変換は、単なる実装時間や一見便利な機能を備えたツールボックスの拡大だけではなく、多くのトレードオフと見なす必要があります。したがって、「依存する」以外の一般的なケースに答えることは非常に困難です。


「標準ライブラリストリームはCの対応物よりも遅い」:おそらく、とにかく、国際化はprintfよりも困難です。
Deduplicator 2015年

1

CとC ++を混在させるのは不適切です。これらは異なる言語であり、実際にはそのように扱う必要があります。最も使いやすいものを1つ選び、その言語で慣用的なコードを記述してみてください。

C ++で多くのコードを節約できる場合は、C ++を使用して、Cパーツを書き直してください。それがあなたが心配しているものであるならば、私はパフォーマンスの違いが顕著であることさえ疑います。


したがって、使用したいこの1つのライブラリがCで書かれていて、使用したい他のライブラリがC ++で書かれています。正常に機能するコードを書き換えると、不要な作業が発生します。
gnasher729

1

私は常にCとC ++の間を行き来し、この方法で作業することを好む変種です。高レベルのものにはC ++を好みますが、Cを鈍い計測器として使用して、データ型をX線分析してビットやバイトのように扱うことができます。たとえば、memcpy低レベルのデータ構造とメモリアロケータを実装するのに便利です。生のビットとバイトのレベルで作業していることに気付いた場合、C ++の本当にリッチな型システムは何の助けにもならないので、Cでそのようなコードを書く方が簡単だと思うことがよくあります。 Cデータ型をビットとバイトとして扱います。

覚えておくべき主なことは、これらの2つの言語がうまくかみ合わないところです。

1. C関数は、C ++関数を呼び出すことはできませんthrow。日常のC ++コードが暗黙的に例外に遭遇する可能性のある場所がいくつあるかを考えると、これは通常、C関数が一般にC ++関数を呼び出してはならないことを意味します。そうしないと、C ++例外をキャッチする実用的な方法がないため、Cコードはスタックのアンワインド中に割り当てたリソースを解放できなくなります。たとえば、CコードでC ++関数をコールバックとして呼び出す必要がある場合、C ++側では、Cコードに戻る前に、発生した例外を確実にキャッチする必要があります。

2.データ型を生のビットとバイトだけとして扱うCコードを記述し、型システムをブルドーザ化する場合(これは、実際には最初にCを使用する主な理由です)、C ++データに対してそのようなコードを使用することは決してありません。コピーコンストラクタとデストラクタ、および仮想ポインタと、尊重する必要があるそのようなものを持つことができるタイプ。したがって、一般的には、この種のCコードを使用するC ++コードではなく、この種のCコードを使用するCコードである必要があります。

C ++コードでこのようなCコードを使用する場合は、通常、それをクラスの実装の詳細として使用して、汎用Cデータ構造に格納するデータが、簡単に作成できるデータ型であることを確認します。そして破壊する。幸いにも、静的アサートでこれを確認するために使用できるこのようなタイプの特性あります。ジェネリックCデータ構造に格納するタイプには、簡単なデストラクタとコンストラクタがあり、そのままです。

それをすべてC ++で書き直そうとする必要がありますか(これにより、新しいC ++テクニックを使用して圧縮できるCスタイルで何かをコーディングした可能性があるC ++オブジェクトとメソッドを実装することを意味します)、または文字列ストリーマーオブジェクトとすべてをCコードに「戻し」ますか?

私の意見では、上記のルールを順守し、作成したテストに対してコードが十分にテストされていることを気にする必要はありません。改善する価値のある部分は、これまでC ++で作成したコードですが、厳密にCベースのコードをC ++に移植する必要があるという意味ではありません。

C ++で記述したコードをC ++コードとしてコンパイルする場合は、注意が必要です。C ++でCのようなコーディングを使用すると、問題が発生します。たとえば、リソースを手動で割り当てたり解放したりすると、コードで例外が発生する可能性があるfreeため、リソースを使用する前に関数から暗黙的に終了する可能性があるため、コードが例外セーフではない可能性があります。C ++では、RAIIやスマートポインターなどは、例外的なパスを考慮せずに通常の実行パスだけを見た場合に表示される可能性があるため、便利ではありません。それらは、正しい例外セーフコードを簡単かつ効果的に記述するための基本的な必要条件であることが多く、例外が発生したときに場所全体にリークが発生するだけではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.