web-dev-qa-db-ja.com

C ++ストリームとCスタイルIO?

IO(printffopenなど)にアクセスするためにCスタイルの操作を使用していることに気付いたとき、小さな趣味のプロジェクト用にC++をコーディングしていました。 。)。

C++プロジェクトにC関数を含めることは「悪い習慣」と見なされますか? CスタイルのIOアクセスよりもストリームを使用する利点は何ですか?

27
gablin

これは白熱したトピックです。

一部の人々は、C++ IOはタイプセーフであり(オブジェクトのタイプとフォーマット文字列で指定されたタイプを区別することはできません)、より自然に流れるため、使用することを好みます。残りのC++のコーディング方法で。

ただし、C IO関数(私の個人的なお気に入り)の引数もあります。それらのいくつかは次のとおりです。

  • ローカライズする文字列全体が小さな文字列に分割されないため、ローカリゼーションとより簡単に統合できます。一部の実装では、ローカライザーは挿入された値の順序を並べ替えて、文字列内で移動できます。
  • 書き込まれるテキストの形式を直接確認できます(これは、ストリーム演算子では非常に難しい場合があります)。
  • インライン化がなく、printf関数のインスタンスが1つしかないため、生成されるコードは小さくなります(これは組み込み環境で重要になる可能性があります)。
  • 一部の実装では、C++関数よりも高速です。

個人的には、C++コードでCストリームを使用することは悪い習慣だとは思いません。 一部の組織 C++ストリームで使用することをお勧めします。私が悪いスタイルだと思うのは、同じプロジェクトで両方を使用することです。ここで重要なのは一貫性だと思います。


他の人が指摘しているように、比較的大規模なプロジェクトでは、おそらくそれらを直接使用することはありませんが、コーディング標準とニーズ(ローカリゼーション、型安全性、タイプの安全性)に最適なラッパー関数(またはクラス)のセットを使用します。 ...)。どちらか一方のIOインターフェースを使用して、この高レベルのインターフェースを実装できますが、おそらく1つしか使用しません。


編集:ローカリゼーションに関連するprintfフォーマット関数ファミリーの利点に関する情報を追加します。これらの情報は、一部の実装でのみ有効であることに注意してください。

%m$の代わりに%を使用して、パラメーターを順番に参照する代わりに、インデックスでパラメーターを参照できます。これは、フォーマットされた文字列の値を並べ替えるために使用できます。次のプログラムは、標準出力にHello World!を書き込みます。

#include <stdio.h>
int main() {
    printf("%2$s %1$s\n", "World!", "Hello");
    return 0;
}

このC++コードの翻訳を検討してください。

if (nb_files_deleted == 1)
    stream << "One file ";
else
    stream << nb_file_deleted << " files ";
stream << removed from directory \"" << directory << "\"\n";

これは本当に難しいかもしれません。 printf(およびローカリゼーションを処理するための gettext のようなライブラリ)を使用すると、コードは文字列と混合されません。したがって、文字列をローカリゼーションチームに渡すことができ、特定の言語で特別な場合がある場合はコードを更新する必要はありません(一部の言語では、オブジェクトの数が0の場合、他の言語では複数形を使用します。 1つは単数形、もう1つは2つのオブジェクトと複数形、...)の3つの形式があります。

printf (ngettext ("One file removed from directory \"%2$s\"",
                  "%1$d files removed from directory \"%2$s\"",
                  n),
        n, dir);
39

printfとその友人は<iostream>に比べて恐ろしく危険であり、拡張することはできません。さらに、もちろんfopenとその友人にはRAIIがありません。つまり、プロファイラーで証明しない限り、パフォーマンスの違いが間違いなく必要である(プラットフォームとコードに存在することが証明されている)場合は、printfのばかである必要があります。

編集:ローカリゼーションは私が考えていなかった興味深いものです。コードをローカライズしたことがなく、printf<iostream>の相対的なローカライズ機能についてコメントすることはできません。

8
Puppy

小さな趣味のプロジェクトでは、おそらくよりタイプセーフなC++ ioストリームを使用します。

面白いことに、私はそれらのいずれかを使用する重要な現実のプロジェクトを見たことがありません。すべての場合において、IO用のネイティブOSAPIの上に構築されたいくつかの抽象化を使用しました。

6

明確な目的がある場合、悪い習慣と見なすことはできません。つまり、IOがプログラムのボトルネックである場合、そうです。CスタイルIOはC++ IOよりも高速に動作します。そうでない場合、I C++ストリームアプローチで行くでしょう。Cuzそれはかわいいです:)

5
Armen Tsirunyan

利点

  • タイプの安全性:C++ストリーミング操作の引数タイプはコンパイル時にチェックされますが、printf引数は...を介して渡され、フォーマットと一致しない場合は未定義の動作を引き起こします。
  • リソース管理:C++ストリームオブジェクトには、ファイルハンドルを閉じたり、バッファーを解放したりするためのデストラクタがあります。 Cストリームでは、fcloseを呼び出すことを忘れないでください。

デメリット

  • パフォーマンス:もちろん、これは実装によって異なりますが、C++ストリームを使用したフォーマットは同等のprintfフォーマットよりもかなり遅いことがわかりました。
5
Mike Seymour

本物のC++プログラマーであるIMHOは、慣用的なC++の方法で物事を実行しようとします。 C変換されたプログラマーは、古いやり方に固執しようとします。それは読みやすさと一貫性に関係しています。

4
grokus

1つは、最初にC++オブジェクト(特にstrings)をC互換のフォームに変換する必要がないことです。

3
geekosaur

C++プロジェクトにC関数を含めることは「悪い習慣」と見なされますか?

いいえ。C関数はC++プロジェクトでよく使用されます。ストリームに関しては、 Google C++スタイルガイド たとえば、「アドホック、ローカル、人間が読める形式で、エンドユーザーではなく他の開発者を対象とする」などの限られた場合にのみ使用することをお勧めします。 。

Cスタイルよりもストリームを使用する利点は何ですかIOアクセス?

主な利点は、型の安全性と拡張性です。ただし、C++ストリームには重大な欠陥があります。ローカリゼーションの問題、不十分なエラーレポート、コードの膨張、一部の実装でのパフォーマンスの問題など、 この質問への回答 を参照してください。

3
vitaut

C++プロジェクトにC関数を含めることは「悪い習慣」と見なされますか?

通常、ファイルIOコードは、クラスまたは関数の実装にカプセル化する必要があります。カプセル化された実装で行った選択を「悪い習慣」とは見なしません。ライブラリまたはコードのユーザー(つまり、インターフェイス)に影響を与えるもののためにその用語を予約します。インターフェースでファイルIOメカニズムを公開している場合、IMOでは、IOストリームまたはCスタイルのIO関数のどちらを使用するかは悪い習慣です。

むしろ、CスタイルのIO関数は(おそらく常に)悪い選択だと言いたいです。

CスタイルのIOアクセスよりもストリームを使用する利点は何ですか?

まず、std::stringなどの標準のC++構造との統合が優れています。それらはSTL<algorithms>とかなりうまく統合されます。また、カプセル化されたカスタム読み取り/書き込み演算子(<<および>>)を作成して、IO操作を実行するときにカスタムクラスがプリミティブ型のように見えるようにすることができます。

最後に、C++のIOストリームは、例外メカニズムを使用して、ストリームまたは読み取り/書き込み操作のエラーを報告できます。これらは、エラーコードメカニズム(各操作の後にエラーコードをチェックするifステートメントと醜いwhileループのシーケンス)のひどい外観を回避することにより、ファイルIOコードをより良くします。

3
Mikael Persson

原則として、C++演算子を使用する必要があります。次のとおりです。

-タイプセーフ。フォーマットがintを必要とする場合にdoubleを渡すリスクはありません。

-拡張可能。独自のインサーターとエクストラクターを作成して使用できます。

-拡張可能。独自のマニピュレータ(アプリケーション固有の論理的意味を持つ)を定義し、それらを使用できます。出力内のすべてのWidgitNumber(内部的にはint)の形式を変更する場合は、マニピュレーターを変更します。 %dがWidgitNumberであるすべてのフォーマットステートメントを見つける必要はありません。

-拡張可能。独自のシンクとソースを作成できます。これらは他のシンクとソースに転送して、必要に応じて入力または出力をフィルタリングまたは拡張できます。

(FWIW:カスタム>>および<<演算子、カスタムマニピュレーター、およびカスタムstreambufを使用しないアプリケーションを作成したことはないと思います。)

3
James Kanze