第 1 章の演習と解答

  • 演習 1-0——章プログラムをテストする
  • 演習 1-1——文字列の連結を理解する
  • 演習 1-2—— 文字列の連結を理解する
  • 演習 1-3—— 範囲を理解する
  • 演習 1-4—— 範囲を理解する
  • 演習 1-5—— 範囲を理解する
  • 演習 1-6——「隠された」std::cin バッファー操作を調べる

1-0 この章のプログラムをコンパイル、実行、テストします。

解決:

プログラム 1:特定の人に挨拶します。人の名前を尋ね、出力として特定の挨拶を生成します。

// ask for a person's name, and greet the person
#include <iostream>
#include <string>
int main()
{
    // ask for the person's name
    std::cout << "Please enter your first name: ";
    // read the name
    std::string name; // define name
    std::cin >> name; // read into
    // write a greeting
    std::cout << "Hello, " << name << "!" << std::endl;
    return 0;
}

7314beb7efbe4573999deeb703e514b5.png

プログラム 2 : より手の込んだ挨拶。5 行の出力が生成されます。最初の行からフレームが始まります。これは、挨拶文と同じ長さの * 文字のシーケンスに、それぞれの末尾にスペースと * を加えたものです。2 行目は、両端に * を付けた適切な数のスペースになります。3 行目は *、スペース、挨拶、スペース、* です。最後の 2 行は、それぞれ 2 行目と最初の行と同じになります。

// ask for a person's name, and generate a framed greeting
#include <iostream>
#include <string>
int main()
{
    std::cout << "Please enter your first name: ";
    std::string name;
    std::cin >> name;

    // build the message that we intend to write
    const std::string greeting = "Hello, " + name + "!";
 
    // build the second and fourth lines of the output
    const std::string spaces(greeting.size(), ' ');
    const std::string second = "* " + spaces + " *";

    // build the first and fifth lines of the output
    const std::string first(second.size(), '*');

    // write it all
    std::cout << std::endl;
    std::cout << first << std::endl;
    std::cout << second << std::endl;
    std::cout << "* " << greeting << " *" << std::endl;
    std::cout << second << std::endl;
    std::cout << first << std::endl;
 
    return 0;
}

b4b61ef95f4e403f85984f46ebde7d29.png

 

1-1 次の定義は有効ですか? なぜ、あるいはなぜそうではないのでしょうか?

const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";

解決:

はい、これらの定義は有効です。1 行目では、長さ 5 文字の文字列変数 hello (これは「Hello」) を定義します。2 行目では、連結演算子を使用して文字列変数メッセージを定義しています。ロジックは次のようになります。

message = ( ( hello + ", world" ) + "!")
        = ( ( a string + a string literal ) + a string literal )
        = ( ( a string ) + a string literal )
        = ( a string + a string literal )
        = ( a string )

この質問に答えるための鍵は、文字列連結演算子 + の使用を認識することです。

  1. 左結合です。
  2. + を使用すると、文字列と文字列リテラル (またはその逆)、または文字列と文字列を連結できますが、文字列と文字列リテラル (またはその逆) を連結することはできません。

このプログラムを実行することで結論を検証できます。

#include <iostream>
#include <string>
int main()
{
    const std::string hello = "Hello";
    const std::string message = hello + ", world" + "!";
    std::cout << message << std::endl;
    return 0;
}

 

1-2 次の定義は有効ですか? なぜ、あるいはなぜそうではないのでしょうか?

const std::string exclam = "!";
const std::string message = "Hello" + ", world" + exclam;

解決:

いいえ、連結演算子の使用は無効です。つまり、文字列リテラル + 文字列リテラルという「タブー」シナリオに遭遇しますが、これは無効です。

  • 行 1 は文字列変数 exclam を定義しており、これは有効です。
  • 2 行目では、連結演算子を使用して文字列変数メッセージを定義しています。ロジックは次のようになります。
message = ( ( "Hello " + ", world" ) + exclam)
        = ( ( a string literal + a string literal ) + a string )
        = ( ( compilation error! ) + a string)

このプログラムを実行することで結論を検証できます。

#include <iostream>
#include <string>
int main()
{
    const std::string exclam = "!";
    const std::string message = "Hello  + ", world" + exclam;
    std::cout << message << std::endl;
    return 0;
}

コンパイル エラーは次のようになります。

error: invalid operands of types 'const char [6]' and 'const char [8]' to binary 'operator+'

 

1-3 次のプログラムは有効ですか?

#include <iostream>
#include <string>
int main()
{
 { const std::string s = "a string";
 std::cout << s << std::endl; }
 
 { const std::string s = "another string";
 std::cout << s << std::endl; }

 return 0;
}

解決:

はい、プログラムは有効です。この質問の鍵は、スコープという用語を理解することです。中括弧の各ペアはスコープを形成します。メイン関数 (スコープ) 内には、2 対の中括弧で定義されている 2 つのサブスコープ (ブロック) があります。各ブロックは独自のスコープを構成します。各スコープ内のすべてのローカル変数とステートメントは互いに独立しています。このため、ブロック 1 に const std::string 変数があっても、 2 つの変数のスコープが異なるため、ブロック 2 に別の等価const std::string 変数を定義しても問題ありません。等価

 

1-4 次のプログラムは有効ですか? 最後から 2 番目と最後から 3 番目の右中括弧の間にセミコロン (;) を追加するとどうなるでしょうか。

#include <iostream>

#include <string>

int main()
{
    {
        const std::string s = "a string";
        std::cout << s << std::endl;
        { 
            const std::string s = "another string";
            std::cout << s << std::endl;
        }
    }
}

解決:

パート 1:

はい、プログラムは有効です。中括弧 {} の各ペアはスコープを形成します。スコープ内にスコープをネストしても問題ありません。わかりやすくするために、これらのスコープを視覚化するためにコードにいくつかのコメントを追加します。

#include <iostream>
#include <string>
int main()
{//scope main starts
    {   //scope main-1 start
        const std::string s = "a string";
        std::cout << s << std::endl;
        {   //scope main-1-1 starts
            const std::string s = "another string";
            std::cout << s << std::endl;
        }  //scope main-1-1 ends
    }  //scope main-1 ends
} //scope main ends

スコープ main-1 のconst std::string 変数は、スコープ main-1-1 (スコープ main-1 内にネストされている) の等価const std::string 変数と同じではありません。等価スコープ main-1-1 がスコープ main-1 内にネストされている場合でも、スコープ main-1-1 内のすべてのローカル変数は main-1 からは隠されています。

特に、文字列は等価内部スコープで上書きされず、外部スコープから引き続き使用できます。

 

パート2:

最後から 2 番目と最後から 3 番目の右中括弧の間にセミコロン (;) を追加しても、有効なプログラムを構成します。わかりやすくするために、セミコロンを使用したプログラムは次のようになります。

#include <iostream>
#include <string>
int main()
{//scope main starts
    {   //scope main-1 start
        const std::string s = "a string";
        std::cout << s << std::endl;
        {   //scope main-1-1 starts
            const std::string s = "another string";
            std::cout << s << std::endl;
        }  //scope main-1-1 ends
        ;  // the additional semi-colon
    }  //scope main-1 ends
} //scope main ends

追加のセミコロンは基本的に main-1 スコープ内に null ステートメントを作成します。つまり、コードには影響しません。著者がこの質問をする理由は、範囲に関する理解を確実にするためだと思います。つまり、そのセミコロンはどのスコープに属しますか? 上記の方法で C++ コードを記述すると、これを簡単に視覚化できます。

 

1-5 このプログラムは有効ですか?

#include <iostream>
#include <string>
int main()
{
    {
        std::string s = "a string";
        {
            std::string x = s + ", really";
            std::cout << s << std::endl;
        }
        std::cout << x << std::endl;
    }
    return 0;
}

解決:

いいえ、プログラムは無効なので修正が必要です。特に、あるスコープは、他のスコープの内部にあるものを「認識」できない場合があります。わかりやすくするために、これらのスコープを視覚化するためにコードにいくつかのコメントを追加します。

// original program with comments added to visualise scope
#include <iostream>
#include <string>
int main()
{ // scope main starts
    { // scope main-1 starts
        std::string s = "a string";
        { // scope main-1-1 starts
            std::string x = s + ", really";
            std::cout << s << std::endl;
        } //scope main-1-1 ends
        std::cout << x << std::endl;
    } // scope main-1 ends
    return 0;
} // scope main ends

外側のスコープ レベルで定義されたすべてのローカル変数は、内側のスコープ (すべてのレベル) で表示または使用できます。ただし、その逆は不可能です。つまり、内側のスコープ レベルで定義されたすべてのローカル変数は、外側のスコープ (すべてのレベル) やそれに隣接するスコープからは参照/使用できません。(つまり、同じレベルのスコープ)。変数の浸透は、外側のスコープから内側のスコープに進みます。同じレベルの他のスコープや内部スコープ (すべてのレベル) には浸透しません。

スコープ main-1-1 は、 std::string 変数 s (外側のスコープ main-1 で定義されている) と、独自に定義された std::string 変数 x を参照できます。

スコープ main-1 は、独自に定義された変数 std::string のみを参照できます。内部スコープ main-1-1 レベルに存在する std::string 変数 x を確認できません。x が何であるかわからないため、「std::cout << x << std::endl」ステップの実行は失敗します。スコープ main-1 の観点から見ると、変数 x は宣言されていません。

スコープ main は、内部スコープ main-1 または main-1-1 で定義された変数を認識しません。わかっているのは、実装が return ステートメントに到達すると、実装が完了するということだけです。

コンパイル エラーは次のようになります。

line(12): error: ‘x’ was not declared in this scope

std::string x をスコープ main-1 から見えるようにするには、中括弧を削除してスコープ main-1-1 を 1 レベル上に移動するだけです。このような:

// corrected
#include <iostream>
#include <string>
int main()
{ // scope main starts
    { // scope main-1 starts
        std::string s = "a string";
        std::string x = s + ", really";
        std::cout << s << std::endl;
        std::cout << x << std::endl;
    } // scope main-1 ends
    return 0;
} // scope main ends

 

1-6 次のプログラムは、入力を求められたときに、一度に 2 つの名前 (たとえば、Samuel Beckett) を入力するとどうなりますか? プログラムを実行する前に動作を予測してから試してください。

#include <iostream>
#include <string>
int main()
{
    std::cout << "What is your name? ";
    std::string name;
    std::cin >> name; // first std::cin step
    std::cout << " Hello, " << name
              << std::endl  << "And what is yours?";
    std::cin >> name;  // second std::cin step
    std::cout << "Hello, " << name
              << "; nice to meet you too!" << std::endl;
    
    return 0;
}

解決:

最初の std::cin ステップで、「Samuel Beckett」という 2 つの単語を入力し、Enter キーを押します。次のようなことが起こります。

(1) 最初に 2 つの単語がバッファ (std::string 変数名に対応) に格納されます。

Samuel Beckett

バッファーは先頭と末尾の空白を自動的に破棄することに注意してください。各単語はスペース文字で区切られます。

(2) 最初の std::cin ステップにより、バッファは最初の単語「Samuel」を std::string 変数割り当てにフラッシュします。std::string 名の値は「Samuel」になりました。最初の単語がバッファからフラッシュされたため、name のバッファは次のようになります。

Beckett

最初の単語 Samuel が消えていることに注意してください。バッファからフラッシュされました。

(3) 2 番目の std::cin ステップでは、バッファー内に既に単語が存在するため、単純にバッファーにその値をフラッシュし、それを std::string 変数名に割り当てるように要求します。std::string 名の値は「Beckett」になりました (古い値「Samuel」は置き換えられました)。これで、name のバッファーは次のようになります。

 

バッファは空になりました。

3 番目の std::cin ステップがあることが判明した場合、ユーザーは、std::cin 機能がバッファーから読み取ることができるように、バッファーにさらに値を指定するように求められます。(ただし、この場合、プログラムには std::cin ステップが 2 つしかありません)。

要約すると、 std::cin は std::string 変数に対応するバッファをチェックします。

  1. バッファー内に少なくとも 1 つの単語が保存されている場合、バッファーから最初の単語がフラッシュされ、その単語が読み取られて std::string 変数に割り当てられます。プログラムを一時停止してユーザーに値の入力を求める必要はありません。
  2. バッファーが空の場合、プログラムは一時停止し、ユーザーにバッファーに値 (単語) を入力するよう求めます。バッファー内にワードがある場合にのみ、 std::cin はそこから読み取り、値を std::string 変数に割り当てます (結果として、そのワードはバッファーからフラッシュされます)。

完全を期すために、プログラムを実行して理解を確認してみましょう。

9f1d0b14dd124f68bdfdb8cad7f120f2.png

特に、最初の std::cin ステップで単語を指定しない場合、Enter ボタンを押してもどこにも到達できないことがわかります。プログラムは、バッファーに少なくとも 1 つの単語を指定するように要求します。

 

おすすめ

転載: blog.csdn.net/ycy1300585044/article/details/132745870