C ++の設計と進化:抜粋

翻訳「30年のC ++」に関するコメントでは、言語の進化のすべての段階が同じようによく知られているわけではなく、特定の構文要素または対応するセマンティクスの起源と開発プロセスについてまったくまったくわからない場合があることに留意されました。 おそらくこの記事は、C ++のより完全な図を形成するために、言語の著者による新しい本ではなく、ずっと前に読者に興味を持たせることができるでしょう。 この本は、その開発がどのように行われたか、このプロセスに影響を与えた理由、およびいくつかのアプローチが他のアプローチよりも好まれた理由を説明しています。

以下は、本からの短い抜粋のセットであり、自由形式で提示され、例によって補足されることもあります。 プレゼンテーションの順序は、基本的に、本での表示の相対的な順序を繰り返します。 番号付けは、個々のアイテムへの可能な参照に使用されます。 これはあまり意味をなさないため、よく知られている事実を意図的に繰り返すことはありません。 それらのあまり知られていない、投稿の著者の観点から最も興味深いものに注意が払われました。 いくつかの点はそれをすべて引用する引用であり、他の多くの点は潜在的に興味深いまたは面白い事実に言及しているだけです。 また、多くの詳細は省略されているため、本で検索する必要があります。

一部の資料は最近の投稿と多少重複していますが、完全を期すためにカットされていません。 順序は無秩序に見えるかもしれませんが、それはサンプルであるため、「混乱」の追加の理由が結論に示されています。 読むとき、この本の最初の版が1994年の初めに出てきたことを覚えておくと便利です。

抜粋


  1. すべては1971年に始まり、 BjörnStraustrupはモデリングに高速言語を必要としていました。

  2. クラスは主にSimulaから来ました。

  3. const修飾子は、 ROMに存在します

  4. デニス・リッチーは、C ++デザインの議論に積極的に関与していました。 (以下では、CとC ++が互いにどの程度影響しているかを確認します。)

  5. 「当初、この言語はプログラムを編成するための一般的なメカニズムを提案しており、特定の主題分野をまったくサポートしていません」、つまり C ++マルチパラダイムは生来のものであり、その著者の考えに対応しています。

  6. 「私は、プログラムを書く正しい方法はひとつもないと心から確信しており、言語設計者はプログラマーに特定のスタイルに従うように強制するべきではありません。」

  7. 初期実装では、仮想関数はサポートされていませんでした。

  8. 言語開発の開始から数年後に、多重継承が追加されました。

  9. 最初は、クラスコンストラクターはnewと呼ばれ、 voidを返しましvoid (ただし、省略できます)。 同時に、オブジェクトにメモリを割り当てたのは設計者であり、デストラクタがそれを解放しました。
     class stack { void new(); // new(); }; 

  10. デストラクタの元の名前はdeleteあり、 void返しましvoid (ただし、省略可能です)。
     class stack { void delete(); // delete(); }; 

  11. 二重コロン( :: :)の代わりに、ドットを使用してクラスメソッドを宣言しました。
     char stack.pop() { // ... } 

  12. クラス名は別の名前空間にあり、 classキーワード(およびCのstruct / union )を前に付ける必要がありました。
     class stack stack; class stack *thatStack = &stack; 

  13. classキーワード自体はSimulaから来ました。Björnは用語の発明のファンではなかったので、彼は以前使用していたものを残しました。

  14. Simula言語では、ヒープ上でのみクラスのインスタンスを作成できましたが、これは非常に不便であり、C ++でスタック上またはグローバルに作成できるというアイデアを与えました。

  15. 「C ++は、システム全体ではなく、システム内の単なる別の言語です。」

  16. 最初の実装では、 thisにアクセスする方法はありませんでしたが、これはエラーの結果でした。

  17. オートプロトタイピング(最初の呼び出しの場所で未知の関数のプロトタイプを表示および保存する)は、もともとC ++で発明されましたが、後にCで使用されました(現在では廃止されています):
     function(1, 1.0); //  ,  function  int  double. function("string"); //    -    int  double. 

  18. (void)を使用した引数を受け入れない関数の宣言は、C ++で最初に追加され、その後Cでのみ追加されました(void) C ++では、この考えはすでに放棄され、空の括弧を使用することに決めました):
     void noArgsInCpp(); void noArgsInCAndCpp(void); 

  19. 暗黙的なintを非推奨として宣言することは、C ++で最初に提案されました。
     function() {} //  ,  function  int. 

  20. 初期の段階では、縮小変換(long- long -> intdouble -> int )を禁止する試みが行われましたが、普及率が高すぎるため、この考えを放棄することにしました:
     void f(long lng, int i) { i = lng; //   . char c = i; //   . } 

  21. 演算子のオーバーロード、リンク、およびブロック内の任意の場所で変数を宣言する機能は、 ALGOl 68に由来します。

  22. 単一行コメント( // )はBCPLからのものです。

  23. 例外の開発に使用されるソース: AdaCluML

  24. テンプレートと名前空間は、Ada言語から借用されています。

  25. 関数宣言でのパラメータータイプの指定-最初にC ++で実装され、後にCで適応されます。

  26. 宣言に代替構文を導入する可能性を検討しました(最近、関連トピックに関する投稿がありました)。
     //  : v: [10]->int; // int *v[10]; p: ->[10]int; // int (*p)[10]; //   : int v[10]->; // int *v[10]; int p->[10]; // int (*p)[10]; int f(char)->[10]->(double)->; // int *(*(*f(char))[10])(double); 
    新しいシンタックスはすべての詳細が解決されたわけではなく、下位互換性を伴う潜在的に大きな問題の背景に対する変更の重要性が小さいため、言語に組み込まれませんでした。

  27. 必須の型接頭辞( struct / union / class )の拒否により、クラスの名前に一致する変数を宣言する可能性が生じました(一般的に、Cとの互換性のため)。
     class Class { }; int main() { Class Class; return 0; } 

  28. かつて、新しい複合型の宣言は、引数リストまたは関数の戻り値で直接許可されていました。
     class A { // ... } get() { return A(); } 

  29. 以前は、基本クラスの「プル」メンバーはusingキーワードの使用を必要としませんでした。単にメンバーの名前を指定するだけで十分でした:
     class Base { protected: void doSomething(); void doSomethingElse(); }; class Derived : public Base { public: doSomething; // using doSomething; Base.doSomethingElse; // using doSomethingElse; }; 

  30. 最初は、クラスだけがfriendなることができました。 この概念の一般的な考え方は、1つのセキュリティドメインに複数のエンティティを配置することであり、そのメンバーの権利は同じです(カプセル化に違反するのではなく、どのようにしてそのような使用の可能性を取り消さないという印象を得ることができますか)。

  31. おそらく、デザイナーのコンセプトを考案したのはストラウストルプでした。

  32. 最初は、各オブジェクトにcall()およびreturn()メソッドを含めることができます。これらのメソッドは、クラスメソッドの実行の前後にそれぞれ呼び出されます。 同様のメソッド:beforeおよび:afterCLOSにあります。
     class ProtectedAccess : object { call(); //   . return(); //  . }; 
    後に、この機能が言語の利点よりも難易度を言語に追加することが決定されました。

  33. 言語にガベージコレクターを含める可能性は数回考えられましたが、受け入れられないことがわかりました。

  34. マルチスレッドの直接サポートも検討されましたが、ライブラリとして実装するために残すことを決定しました。

  35. しばらくの間、混乱を避けるために言語はC84と呼ばれていました。ユーザーがCを「new C」や「improved C」などのクラスに置き換えたためです。 Cが1985年に標準化されることを楽観的に考えて、ビョルンは「標準C」とのあいまいさを避けるために、名前を再度変更するように求められました。

  36. 最初に使用されたプリプロセッサではなく、最初のC ++コンパイラはC ++で記述されていました(Cfront、C with classesおよびC84はCで記述されているようです)。

  37. CfrontはおそらくCコードを生成する最初の部分サイクルコンパイラであり、その後にAda、 EiffelLispModula-3Smalltalkが続きました。

  38. 最初の例外実装は、1992年にHewlett-Packartによって追加されました。

  39. 仮想機能はSimulaから借用されましたが、変更が加えられました。

  40. ランタイムタイプの定義は、当初、ユーザーが本質的にswitchであるランタイムタイプではなく、静的なタイプコントロールと仮想関数を使用することを強制するために意図的に追加されたものではありません。

  41. struct 、均一性と統一性を目的として、 class structほぼ同等です。

  42. Cfront 2.0より前は、 operator=はグローバル関数でしたが、定義済みのセマンティクスが競合するため、後でこの考えを捨てました。
     class C {}; C & operator=(C &rhs) {} //   : // src.cpp:3:21: error: 'C& operator=(C&)' must be a nonstatic member function 

  43. 使用ポイントでの変数の定義は、ALGOL 68から取得されます。

  44. リンクもALGOL 68から借用されていますが、初期化後に再割り当てされる可能性があります。

  45. 左辺値/右辺値によるオーバーロードは、Cfront 1.0の時代に遡ると考えられていました。

  46. 読み取り専用/書き込み専用のポインター/データは、1981年にStraustrupとDennis Ritchieによって発明され、後にANSI C委員会(X3J11)によってC標準に追加されましたが、やや切り捨てられた形式です。

  47. キーワードnewもSimulaから取られています。

  48. オブジェクト配置の最初の実装は、 thisをコンストラクタに割り当てることでした。

  49. constructor / destructorは、キーワードの数を減らすため、およびより明確な構文のために、対応する特別なメソッドの名前として拒否されました。

  50. Cのクラスのnew()およびdelete()関数(古いコンストラクターとデストラクタ名)は、デフォルトでpublicアクセス修飾子を自動的に受け取りました。

  51. ::演算子は、ピリオドのあいまいさを解決するために導入されました。

  52. 初期には、初期化リストに入力された変数は彼の体の後に表示されていました(不正確な文言「名前は宣言の場所からフィールドの終わりまで表示されているため」)。 Borland C ++でこれに遭遇した人は多いでしょう。
     for (int i = 0; i < n; ++i) { // ... } if (i >= n) { //   i,    for. 

  53. ネストされたクラススコープが追加され、後でC互換性のために削除され、再度追加されました。
     //      C   C++: struct Outer { struct Inner { }; }; //   C   : struct Outer { }; struct Inner { }; 

  54. デフォルトでは、グローバル文字のstaticはCのルールに違反していたため、C ++には追加されませんでした。

  55. Cで関数を呼び出すときの厳密な型制御は、C ++から来ました。

  56. 「優れたデザインの鍵は、最先端のアイデアを取り入れるのではなく、言語が直面している課題を深く理解することです。」

  57. 抽象クラス、タイプセーフレイアウト、および多重継承がCfront 2.0で登場しました。

  58. クラスメンバーの宣言のランダムな順序は問題を引き起こし、型とデータの間に不平等をもたらしました。 許可されています:
     int x; class X { int f() { return x; } // x  X::x int x; }; 
    そして、それはすでに禁止されています:
     typedef char *T; class Y { T f() { T a = 0; return a; } // , .. T      . typedef f int T; }; 
    次の理由から、関数型についても同じことが言えます。
     typedef int P(); class X { static P(Q1); // static int Q1(); static P Q2; // static int Q2(); }; 

  59. Cfrontでは、ブロックの最後で一時オブジェクトが破棄されました(現在-一時オブジェクトがバインドされていない場合、式を評価した後):
     const char *p; std::string s1 = ..., s2 = ...; if ((p = (s1 + s2).c_str()) && p[0]) { //      p. // ... //    ,   "s1 + s2". } 

  60. まだ誰かにとってはニュースかもしれませんが、PascalにはISO / IEC規格もあります(そのような言語はいくつかあります)。

  61. 名前付き引数を追加する提案は検討されましたが、拒否されました。 (この本を読んだときの印象は、かなり多数の異なる提案が拒否されたということでした;しかし、この多くは数十年後にまだ標準になりました。)

  62. restrict (noalias)ポインターは、90年代前半に知られていましたが、Cからは決して適応しませんでした。

  63. ヨーロッパでは、7ビットエンコーディングが広く使用されているため、特殊文字がC配布の問題を引き起こしました。 ここから、3文字表記、2文字表記、および特殊な単語( andor 、など。 これはすべてC ++に移行されました。

  64. AT&TがC ++で割り当てた予算は、全体で約3000ドルです。 これらのうち、1000ドルはUNIX顧客へのC ++広告の配布に、2000ドルはすべてが控えめな最初の会議に行きました(十分な紙ではなくても、ボランティアは訪問者登録フォームの技術文書のコピーを作成しました...)。 それにもかかわらず、何十年にもわたっていかなる種類のマーケティングも存在しなかったため、この言語の普及は妨げられませんでした。

おわりに


結論として、各部分の詳細レベルが上がる本のやや非標準的な構造(したがって、アイテムの順序は奇妙に見えるかもしれません)、およびStraustrupが固守しようとしている原則とその根拠の概要を述べたいと思います。 したがって、C ++プログラマーだけでなく、プログラミング言語全般に関心を持つ多くの人にとっても興味深いはずです。

Source: https://habr.com/ru/post/J270445/


All Articles