ブログ読者の誤解

あなたの C++ 神話にとても興味がありました。特に、私のドイツ語の読者は非常に活発でした。いくつかの電子メールを受け取り、Heise Developer に関する活発な議論を観察しました。

あなたの神話について書く前に、まず C++ コア ガイドラインの神話をまとめさせてください。これが最後の神話です。

NR.7:禁止:すべてのデータ メンバーを protected にする

保護されたデータにより、プログラムが複雑になり、エラーが発生しやすくなります。保護されたデータを基本クラスに入れると、派生クラスを分離して推論することはできず、したがってカプセル化を破ることになります。クラス階層全体について常に推論する必要があります。

つまり、少なくともこれら 3 つの質問に答える必要があります。

<オール>
  • 保護されたデータを初期化するために、派生クラスにコンストラクターを実装する必要がありますか?
  • 保護されたデータを使用した場合、そのデータの実際の価値は?
  • 保護されたデータを変更すると誰が影響を受けますか?
  • これらの質問への回答は、クラス階層が深くなるほど複雑になります。

    考えてみると、保護されたデータは、クラス階層のスコープ内の一種のグローバル データです。そしてご存知のように、変更可能な共有状態はひどいものです。たとえば、テストと並行性が非常に難しくなります。

    さて、私はあなたの神話に切り替えます。受け取った順に書いています。

    C++ で書かれたプログラムは、より多くのメモリと CPU を必要とするため、 C で書かれたプログラム (Gunter Königsmann)

    まず最初に:古典神話について Gunter Königsmann に感謝します。

    正直なところ、この神話に反論することは非常に難しく、一般的に C++ 標準ライブラリでは不可能です。これが理由です。この章の最後にいくつかの所見を追加します。まず第一に、ここに厳然たる事実があります。 「C++ のパフォーマンスに関するテクニカル レポート」はとても役に立ちます。

    C++ パフォーマンスに関するテクニカル レポート

    ワーキング グループ WG 21 は、2006 年に論文 ISO/IEC TR 18015 を発行しました。タイトルはあまり面白くないように聞こえますが、C++ 機能のパフォーマンス数値を取得したい場合、このドキュメントは究極の情報源です。この文書は、その懸念を直接的に表現しています。

    • さまざまな C++ 言語およびライブラリ機能の使用によって暗示される時間とスペースのオーバーヘッドのモデルを読者に提供する
    • パフォーマンスの問題について広まっている神話を暴くため
    • パフォーマンスが重要なアプリケーションで C++ を使用するためのテクニックを紹介する
    • C++ 標準言語とライブラリ機能を実装して効率的なコードを生成するテクニックを紹介する

    200 ページを超える論文の著者は、Dave Abrahams、Howard Hinnand、Dietmar Kühl、Dan Saks、Bill Seymour、Bjarne Stroustrup、Detlef Vollmann などの有名な C++ 専門家です。

    ドキュメントの範囲は、C++ の機能、そのオーバーヘッドと使用法、C++ での効率的なライブラリの作成、組み込みシステムでの C++ の使用法、およびハードウェアと通信するための C++ のインターフェイスです。

    C++ の機能、オーバーヘッド、および使用法

    著者は、5 つの異なるコンパイラを備えた 3 つのコンピューター アーキテクチャを分析に使用しています。彼らは異なる最適化レベルのコンパイラを使用しています。非常に注目に値する結果のアイデアだけをお伝えします。

    • 名前空間
      • サイズとパフォーマンスに大きなオーバーヘッドがない
    • 型変換演算子
      • C++ のキャスト const_cast、static_cast、および reinterpret_cast は、サイズもパフォーマンスも C pedant と変わりません。
      • 実行時に実行される dynamic_cast にはオーバーヘッドがあります . (注:変換には C pedant はありません。)
    • 継承
      • クラス
        • 仮想関数を持たないクラスは、構造体と同じ大きさです。
        • 仮想関数を持つクラスには、ポインターと仮想関数テーブルのオーバーヘッドがあります。これらは約 2 ~ 4 バイトです。
      • 関数呼び出し
        • 非仮想、非静的、および非インライン関数の呼び出しは、フリー関数の呼び出しと同じくらいコストがかかります。
        • 仮想関数の呼び出しは、テーブルに格納されているポインターを使用するフリー関数の呼び出しと同じくらいコストがかかります。
        • クラス テンプレートの仮想関数は、サイズのオーバーヘッドを引き起こす可能性があります。
        • インライン化 関数を使用すると、パフォーマンスが大幅に向上し、C マクロのパフォーマンスに近くなります。
      • 多重継承
        • 時間やスペースのオーバーヘッドが発生する可能性があります。
        • 仮想基本クラスには、非仮想基本クラスと比較してオーバーヘッドがあります。
    • ランタイム型情報 (RTTI)
      • 必要なクラスごとに約 40 バイトが追加されます。
      • typeid 呼び出しは非常に遅いです。これは、実装の品質によるものと思われます。
      • dynamic_cast を使用した実行時の変換が遅い。レポートによると、実装の品質にも起因するはずです。
    • 例外処理
      • 例外を処理する方法は 2 つあります。これらはコードとテーブル戦略です。コード戦略では、例外を処理するために追加のデータ構造を移動および管理する必要があります。テーブル戦略には、テーブルに実行コンテキストがあります。
        • コード戦略には、スタックとランタイムのサイズ オーバーヘッドがあります。実行時のオーバーヘッドは約 6% です。このオーバーヘッドは、例外がスローされなくても存在します。
        • テーブル戦略には、プログラムのサイズや実行時のオーバーハンドはありません。 (注:そのステートメントは、例外がスローされなかった場合にのみ保持されます。)テーブル戦略は実装がより困難です。
    • テンプレート
      • テンプレートのインスタンス化ごとに、新しいクラス テンプレートまたは関数テンプレートを取得します。したがって、単純にテンプルを使用すると、コードが肥大化する可能性があります。最新の C++ コンパイラは、テンプレートのインスタンス化の数を大幅に削減できます。部分的または完全な特殊化を使用すると、テンプレートのインスタンス化を減らすことができます。

    詳細、正確な数、いくつかの追加トピックは、レポートで直接読むことができます:TR18015.pdf.

    まだ納得できませんか?以下は MISRA C++ からの引用です:

    MISRA C++

    MISRA C++ (M オトル 業界 S ソフトウェア R 適性 A ssociation) は、セーフティ クリティカル システムのガイドライン ソフトウェアを作成しています。もともとは自動車産業向けに設計されたもので、航空、軍事、医療分野のデファクト スタンダードになりました。問題は、MISRA が C++ について何と述べているかです。

    MISRA は、重要なシステムにおける C++ の重要性がますます重要になっている理由を強調しています。 (1.1 重要なシステムでの C++ の使用):

    • C++ は、多くの組み込みシステムに不可欠な、高速で低レベルの入出力操作を適切にサポートします。
    • アプリケーションの複雑さが増しているため、アセンブリ言語よりも高級言語を使用する方が適切です.
    • C++ コンパイラは、C と同様のサイズと RAM 要件でコードを生成します。

    しかし、1 つの小さなダウナーが残っています。 MISRA C++ は、従来の C++ に基づいています。これは単純に C++98 です。最新の C++ には、組み込みシステム向けに提供できる機能が他にもたくさんあります。この意見は、MISRA C++ だけでなく、前述の C++ パフォーマンスに関するテクニカル レポートにも当てはまります。

    最新の C++ についていくつか考察せずに私の投稿を締めくくるわけにはいきません。最新の C++ は、C++11、C++14、および C++17 の 3 つの C++ 標準に対して主に使用される用語です。

    私の観察

    C++ 標準ライブラリとその C ペンダントを比較する際の難しさは、特に、同等のデータ構造またはプログラムを比較する必要があることです。つまり、C 文字列と C++ 文字列を比較することはできません。後者は自動メモリ管理をサポートしているためです。 C++ コンテナーとは対照的に、C 配列にも同じことが当てはまります。これ以上苦労することなく、ここに私のポイントがあります。さらに興味がある場合は、上記の投稿をお読みください。

    • std::array は設計ごとに C 配列と同じくらい高速かつ安価であり、さらにその長さを知っています。C++ コア ガイドライン:std::array と std::vector はあなたの友達です。
    • std::unique_ptr は、生のポインターと同じくらい高速かつ安価な設計です。もちろん、std::unique_ptr は設計ごとに安全です:スマート ポインターのメモリとパフォーマンスのオーバーヘッド。
    • 型特性ライブラリを使用すると、それ自体を最適化するコードを記述できます。型特性:パフォーマンスが重要です。
    • constexpr のおかげで、コンパイル時にコストのかかる計算ジョブを実行できます:C++ コア ガイドライン:constexpr を使用したコンパイル時のプログラミング
    • 移動セマンティックな完全転送により、高価で安全でないコピー操作を安価で安全な移動操作に置き換えることができます。一般に、移動操作ではなくコピー操作が失敗する可能性があります。コピーと移動のセマンティック:少数の数字と完全転送

    次は?

    わお?メモリとパフォーマンスの神話に反論するのに、ほぼ 1 本の投稿が必要でした。私が得た追加の神話を解明しなければならないことは想像できるでしょう。このメールアドレスはスパムロボットから保護されています。表示するには JavaScript を有効にする必要があります..