Tag Archives: C++

Accelerated C++ Solution to Exercise 2-8

Exercise 2-8

Write a program to generate the product of the numbers in the range [1,10).

Solution

The question asks as to compute the product of the numbers within the asymmetric range [1,10). In other words, the product of numbers within the range of 1 to 10, excluding 10. i.e.

1 x 2 x 3 ... x 7 x 8 x 9

Observation: this is the same as 9 factorial (9!). This is the same logic as computing n factorial.

Here outlines my solution strategy at the top level.

Solution Strategy

Descriptions:

  • Fact: the product of 3 x 4 x 5 (ascending syntax) is the same as 5 x 4 x 3 (descending syntax). So for simplicity, we restrict ourself by only writing in an ascending syntax. i.e. use [3,6), and avoid [5,1) . This is the same as making the assumption of n >= m.
  • Fact: Case 1, if (n-m)=0, e.g. the asymmetric range is [3,3), this is equivalent of saying the range is between 3 and 3 (excluding 3). Because this range is null, the product is also null (zero). No loops required.
  • Fact: Case 2, if (n-m)=1, e.g. the asymmetric range is [3,4), this is equivalent of saying the range is between 3 and 4 (excluding 4). The range is therefore just the number 3 (i.e. m). Likewise, the product is also 3 (i.e. m). No loops required.
  • Fact: Case 3, if (n-m)>=1, e.g. the asymmetric range is [3,7), this is equivalent of saying the range is between 3 and 7 (excluding 7). i.e. we are looking for the product of 3 x 4 x 5 x 6. To compute this we must use loops. The main focus of this post is case 3.

Case 3 Solution Strategy

After spending an entire Sunday afternoon on this question, I eventually spotted a pattern which I believe is consistent and can be leveraged in the forming of our algorithm. To demonstrate this pattern, I have put together some diagrams in PowerPoint and presented as snapshot images below. For clarity, we are going to focus on this particular (arbitrary) asymmetric range as our core example: [3, 7).

Solution Strategy

 

Observations:

  • The number of elements is always (n-m). there are 4 elements in this case.
  • The number of loops required is always (n-m-1). We need 3 loops in this case. (numLoops = 3).
  • The number of loops required also equals to the number of (multiplication) operator.
  • Knowing the number of loops required enable us to form the loop invariant i [0,numLoops).

Now, let’s try and observe more patterns – what happens at each loop? (See diagram below)

Solution Strategy

Observations:

  • 4 major variables are required.
  • The integer invariant i that controls whether the loop is to go ahead or terminate.
  • The integer variable a – the lower number of the product (a x b).
  • The integer variable b – the higher number of the product (a x b).
  • The integer variable c – the product of (a x b).
  • At the beginning of the very first loop, initialise values of a and b.
  • At the end of each loop, the re-initialise values of a and b.

The final product of [m,n) is the value of c at the end of the very last loop.

For completeness, let’s do a drill down into the loops in greater depth. Bear in mind that for [3,7), the numLoops is n-m-1=3. We need 3 loops. The loop go ahead if the invariant i has not reached numLoops – it starts at 0 (zero), and get increment by 1 at end of each loop.

Invariant i = 0 (Loop)

Solution Strategy

Invariant i = 1 (Loop)

Solution Strategy

Invariant i = 2 (Loop)

Solution Strategy

Invariant i = 3 (No Loop)

Solution Strategy

At the end of the loop, output the final result. The product of the numbers within the asymmetric range [m,n) is the value of c at the end of the very last loop. The following table summarises this looping process.

Invariant i a b c
0 3 4 12
1 12 5 60
2 60 6 360

Full Program

We should now have all the fundamentals in the making of our program. This is my version of the program. (There are many ways to do this however!)

#include <iostream>
using namespace std;
int main()
{
    cout << "***********************************************************\n"
         << "*** This program computes the product of the numbers    ***\n"
         << "*** in the asymmetric range [m,n).                      ***\n"
         << "*** (Limitation: please ensure m is not larger than n)  ***\n"
         << "*** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) ***\n"
         << "***********************************************************";
    cout << endl;

    // Ask user to supply the startNumber
    cout << "Enter m: ";
    int m;
    cin >> m;

    // Ask user to supply the endNumber
    cout << "Enter n: ";
    int n;
    cin >> n;

    // Algorithm to compute the product of numbers within the asymmetric range [m,n)
    const int startNumber = m ;
    const int endNumber = n;
    const int numElements = endNumber - startNumber;
    const int numLoops = numElements - 1;
    int c;
    if (numElements == 0)
        {c = 0;}
    else if (numElements == 1)
        {c = startNumber;}
    else
    {
        int a = startNumber;
        int b = startNumber + 1;

        cout << " i, a, b, c " << endl;
        for (int i = 0; i != numLoops; ++i)
        {
            c = a * b;
            cout << i << ", " << a << ", " << b << ", " << c << endl;
            a = c;
            ++b;
        }
    }
    cout << "Asymmetric range: [" << startNumber << "," << endNumber << ")" << endl;
    cout << "Number of elements: " << numElements << endl;
    cout << "Number of loops required: " << numLoops << endl;
    cout << "Product of numbers within the asymmetric range = " << c << endl;
    return 0;
}

Result

The program is meant to be robust and be able to deal with any asymmetric range [m,n) as long as (n>=m). Case 1-3 are mainly for prove of concepts. Case 4 is the solution to the question (find product of numbers within the range [1,10).

  • Case 1 – when (n-m)=0. e.g. [3,3)
  • Case 2 – when (n-m)=1. e.g. [3,4)
  • Case 3 – when (n-m)>1. e.g. [3,7)
  • Case 4 – when (n-m)>1. e.g. [1,10)

Case 1 – Asymmetric Range [3,3)

***********************************************************
*** This program computes the product of the numbers    ***
*** in the asymmetric range [m,n).                      ***
*** (Limitation: please ensure m is not larger than n)  ***
*** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) ***
***********************************************************
Enter m: 3
Enter n: 3
Asymmetric range: [3,3)
Number of elements: 0
Number of loops required: 0
Product of numbers within the asymmetric range = 0

Case 2 – Asymmetric Range [3,4)

***********************************************************
*** This program computes the product of the numbers    ***
*** in the asymmetric range [m,n).                      ***
*** (Limitation: please ensure m is not larger than n)  ***
*** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) ***
***********************************************************
Enter m: 3
Enter n: 4
Asymmetric range: [3,4)
Number of elements: 1
Number of loops required: 0
Product of numbers within the asymmetric range = 3

Case 3 – Asymmetric Range [3,7)

***********************************************************
*** This program computes the product of the numbers    ***
*** in the asymmetric range [m,n).                      ***
*** (Limitation: please ensure m is not larger than n)  ***
*** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) ***
***********************************************************
Enter m: 3
Enter n: 7
 i, a, b, c
0, 3, 4, 12
1, 12, 5, 60
2, 60, 6, 360
Asymmetric range: [3,7)
Number of elements: 4
Number of loops required: 3
Product of numbers within the asymmetric range = 360

Case 4- Asymmetric Range [1,10)

***********************************************************
*** This program computes the product of the numbers    ***
*** in the asymmetric range [m,n).                      ***
*** (Limitation: please ensure m is not larger than n)  ***
*** e.g. [3,7) contains elements 3, 4, 5, 6 (but not 7) ***
***********************************************************
Enter m: 1
Enter n: 10
 i, a, b, c
0, 1, 2, 2
1, 2, 3, 6
2, 6, 4, 24
3, 24, 5, 120
4, 120, 6, 720
5, 720, 7, 5040
6, 5040, 8, 40320
7, 40320, 9, 362880
Asymmetric range: [1,10)
Number of elements: 9
Number of loops required: 8
Product of numbers within the asymmetric range = 362880

Compute the N factorial

Note that this program may also be used as a skeleton program for computing factorial. e.g. 5! = 1 x 2 x 3 x 4 x 5. In other words, to compute n! this is effectively the same as computing the internal product of the numbers within the asymmetric range (0, n+1).

Conclusion

The product of the numbers within the asymmetric range [0,10) is 362880, as computed by the program (and verified by calculators). This program might also be used as a skeleton program for computing factorial.

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-7

Exercise 2-7

Write a program that count down from 10 to -5

Solution

Though it is tempting to just create a for loop (or a while loop) with an invariant that start from 10, output the invariant, increment the invariant by -1, and exit the loop when the invariant becomes smaller than -5, I have decided to write the program in a way that is consistent with the asymmetric range method that the author has taught us. i.e. make use of the asymmetric invariant range denotation i[0,End) – meaning creating an invariant i that start with the value of zero (and always zero!), with an end number representing the number of loops that we wish the invariant to go through.

So how do we find out how many loops we need?

  • If we are to only output 10 we need 1 loop (to display 1 number). 10 – 10 + 1 = 1. i.e. we need the asymmetric range of [0,1)
  • If we need to output 10 and 9, we need 2 loops (to display 2 numbers). 10 – 9 +1 = 2. i.e. we need the asymmetric range of [0,2)
  • If we need to output 10, 9, 8, we need 3 loops (to display 3 number). 10 – 8 + 1 = 3. i.e. we need the asymmetric range of [0,3)

See the pattern?

If we need to display N numbers (counting down), we need N loops, as calculated as:

N = (( EndNumber - StartNumber ) * (-1 ) ) + 1

So if we are to display 10, 9, 8 …. -3, -4, -5, we have:

  • StartNumber = 10
  • EndNumber = -5
  • NumLoops = (( EndNumber – StartNumber ) * (-1 ) ) + 1 = ( (-5 -10) * (-1) ) + 1 = 15 + 1 = 16 loops.

Our asymmetric invariant range therefore takes this denotation: i [0, 16)

Understanding these relationships will allow us to be consistent in the way we program. This knowledge will enable us to be robust in solving a problem like this: write a program that count down from 999 to – 777. i.e. we can simply repeat the above methodology (or algorithm) without scratching our heads and get stuck!

One more question before we move on, what happen if we are counting up instead of counting down?

e.g. if we are to display -5, -4, -3, … 8, 9, 10, we have:

  • StartNumber = -5
  • EndNumber = 10
  • NumLoops = ( ( EndNumber – StartNumber ) * 1 ) + 1 = ( ( 10 – (-5) ) * 1 ) + 1 = 15 + 1 =16 loops.

If we need to display N numbers (counting up), we need N loops, as calculated as:

N = (( EndNumber - StartNumber ) * (+1 ) ) + 1

Our asymmetric invariant range still take the same denotation: i [0, 16)

The general equation for NumLoops is therefore:

NumLoops = ( (EndNumber - StartNumber) * directionFlag ) + 1
  • If counting up (i.e. EndNumber > StartNumber), the directionFlag is 1.
  • Otherwise, the directionFlag is -1.

Quite initiative really as we have a positive flag for counting up, and a negative flag for counting down.

Also note that if EndNumber == StartNumber, directionFlag becomes irrelevant because in that case the numLoops will be (0 * directionFlag) + 1, which is always 1.

The directionFlag also provides us a mean to determine whether we wish to increment the output value by +1 or -1.

The last bit is the actual counting and displaying the numbers. This is how I’d do it. (Note: there are many ways to do this!)

// Do the counting and display result row by row
for (int i = 0, iOut = startNumber; i != numLoops; ++i)
{
    std::cout << "Loop " << i << "| iOut = " << iOut << std::endl;
    iOut += directionFlag;
}

This is a for loop with i [0,numLoops) denotation. i.e. we are certain that there will be numLoops number of loops in total.

The first loop output the start value. At the end of the loop we increment the invariant by either +1 (counting up), or -1 (counting down). If the condition (i != numLoops) is true, loop again. Otherwise, the loop terminates.

The above snippet essentially isolate the invariant i component from the computed iOut component – for flexibility. In other words, the function of the invariant is to purely count the number of completed loops. We leave the computed component as free as it can be.

Putting everything together, we now have our full program:

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main()
{
    // Display greeting message
    cout << "*****************************n"
         << "*** The Counting Program ****n"
         << "*****************************n";

    // Obtain startNumber and endNumber from the user
    cout << endl;
    cout << " Enter Start Number: ";
    int inputStartNumber;
    cin  >> inputStartNumber;
    cout << " Enter End Number: ";
    int inputEndNumber;
    cin  >> inputEndNumber;

    const int startNumber = inputStartNumber;
    const int endNumber = inputEndNumber;

    // Compute the directionFlag and numLoops
    int directionFlag, numLoops;
    if (endNumber > startNumber) {directionFlag = 1;}
    else {directionFlag = -1;}

    numLoops = ( (endNumber - startNumber) * directionFlag ) + 1;

    // Display the directionFlag and numLoops
    cout << " directionFlag = " << directionFlag
         << " | numLoops = " << numLoops << endl;

    // Do the counting and display result row by row
    for (int i = 0, iOut = startNumber; i != numLoops; ++i)
    {
        std::cout << "Loop " << i << "| iOut = " << iOut << std::endl;
        iOut += directionFlag;
    }
    return 0;
}

Result

The code compile okay. Let’s run it and see what we get!

Case 1: count from 10 to -5

*****************************
*** The Counting Program ****
*****************************

 Enter Start Number: 10
 Enter End Number: -5
 directionFlag = -1 | numLoops = 16
Loop 0| iOut = 10
Loop 1| iOut = 9
Loop 2| iOut = 8
Loop 3| iOut = 7
Loop 4| iOut = 6
Loop 5| iOut = 5
Loop 6| iOut = 4
Loop 7| iOut = 3
Loop 8| iOut = 2
Loop 9| iOut = 1
Loop 10| iOut = 0
Loop 11| iOut = -1
Loop 12| iOut = -2
Loop 13| iOut = -3
Loop 14| iOut = -4
Loop 15| iOut = -5

Case 2: count from -5 to 10

*****************************
*** The Counting Program ****
*****************************

 Enter Start Number: -5
 Enter End Number: 10
 directionFlag = 1 | numLoops = 16
Loop 0| iOut = -5
Loop 1| iOut = -4
Loop 2| iOut = -3
Loop 3| iOut = -2
Loop 4| iOut = -1
Loop 5| iOut = 0
Loop 6| iOut = 1
Loop 7| iOut = 2
Loop 8| iOut = 3
Loop 9| iOut = 4
Loop 10| iOut = 5
Loop 11| iOut = 6
Loop 12| iOut = 7
Loop 13| iOut = 8
Loop 14| iOut = 9
Loop 15| iOut = 10

Case 3: count from 10 to 10

*****************************
*** The Counting Program ****
*****************************

 Enter Start Number: 10
 Enter End Number: 10
 directionFlag = -1 | numLoops = 1
Loop 0| iOut = 10

 Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-6

Exercise 2-6

What does the following code do?

int i = 0;
while (i < 3)
{
    i += 1;
    std::cout << i <<std::endl;
}

Solution

This is very a classic usage of while loop with the following properties:

Invariant i, with the initial integer value of zero.

int i = 0;

And asymmetric range [0,3). i.e. invariant i is incremented at a specified interval (in this case 1) from the initial value of 0 to the ending value of 3 (but excluding 3). Note the mixed use of square and round brackets (in the sense of writing in the post rather than the actual code.). We use square brackets [ ] to imply inclusiveness, and round bracket ( ) to imply exclusiveness.

while (i < 3)    // asymmetric range of [0,3)
{
    i += 1;     // invariant increment interval is 1
    std::cout << i <<std::endl;   // display the invariant to the standard output console
}

I denote this while loop as i [0,3)

Using logic, I’d expect the code to do this:

Invariant i (at top of loop) while ( i < 3) i += 1 Console Output
0 true 1 1
1 true 2 2
2 true 3 3
3 false

To confirm this let’s run the following code and output result.

#include <iostream>

int main()
{
     int i = 0;
     while (i < 3)
     {
         i += 1;
         std::cout << i <<std::endl;
         //i += 1;
     }
     return 0;
}

Result

1
2
3

The nice thing about using asymmetric range [0,N) is the ease of understanding. i.e. The invariant is true for N number of loops.

Extras

Say now if we modify the code so that the invariant increment happens at the bottom. i.e.

int i = 0;
while (i < 3) {
    std::cout << i <<std::endl;
    i += 1;
}

Our result should now look like this:

Invariant i (at top of loop) while ( i < 3) Console Output i +=1
0 true 0 1
1 true 1 2
2 true 2 3
3 false

We run the modified code and it confirms our expectation:

0
1
2

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-5

Exercise 2-5

Write the set of “*” characters so that they form a square, a rectangle, and a triangle.

Solution

This is a fun one! To make this an even more interesting problem to solve, I’m going to add one more requirement – hollow and solid shapes. This diagram outlines my solution strategy:

Solution Stategy

In this post I will:

  • Walk through the solution strategy step-by-step, with explanation of the code snippets that I have created using logics.
  • Put all the snippets together and form one complete program.
  • Run the complete program and record the results – seeing is believing!

I will start from step one of my solution strategy.

1 – Choose Shape Type

Shape Type

The program first asks the user to select the int inputShapeType 1 (Square), 2 (Rectangle), or 3 (Triangle). This is stored as the const int shapeType which has global scope. I’ve also added a for loop for the extra effect that, in the case that the user type in a number outside the selected range (e.g. 9), the program would keep re-asking the question until the user eventually type in the valid range of 1, 2, or 3. Note that if we really want we could add extra controls such as preventing the user from entering letters (instead of numbers). But for the sake of this exercise.

        // ask for the Shape Type
        int inputShapeType;
        for (int keepAsking = 1; keepAsking == 1; )
        {
            cout << "Select the Shape Type." << endl;
            cout << "  (1) - Square " << endl;
            cout << "  (2) - Rectangle " << endl;
            cout << "  (3) - Triangle " << endl;
            cout << "Shape Type (enter 1/2/3): ";
            cin >> inputShapeType;

            // Make sure the user input is within range. If not, ask again!
            if (inputShapeType == 1 || inputShapeType == 2 || inputShapeType == 3)
                {keepAsking = 0;}
        }
        const int shapeType = inputShapeType;

2 – Define Shape Height

Shape Height

We ask the user to supply the desirable height (in number of columns). This integer height is stored as a int const numRows and has global scope.

        // ask for the shape height
        cout << "Enter Shape Height: ";
        int shapeHeight;
        cin >> shapeHeight;

        // set the total number of rows to write (as a constant)
        int const numRows = shapeHeight;    

3 – Define or Compute Shape Width

Shape Width

  • If the shape is a square, the width is computed as the same as height.
  • If the shape is a rectangle, the user will need to explicitly supply the width (in number of columns).
  • If the shape is a triangle, the width is computed as: width = (2*numRows)-1.

We store the width as const string:size_type numCols.

        // Only need to ask for a width if the shape is a a rectangle
        // For square and triangle, the width is computed instead.
        int shapeWidth;
        if (shapeType == 2)
        {
            // ask for the shape width
            cout << "Enter Shape Width: ";
            cin >> shapeWidth;
        }

        // compute the number of columns to write (as a constant) based on shape type
        int finalShapeWidth;
        if (shapeType == 1)
            {finalShapeWidth = shapeHeight; }    // Compute square width
        else if (shapeType == 2)
            {finalShapeWidth = shapeWidth; }     // Define rectangle width
        else
           {finalShapeWidth = (2*numRows)-1; }   // Compute triangle width
        const string::size_type numCols = finalShapeWidth; // define constant here  

4 – Choose Fill Style (Hollow / Solid)

Fill Style

To make the problem more interesting I have added the option to either output a hollow or solid shape. We ask user to choose option 1 (Hollow), or 2 (Solid) and store the value as int inputFillStyle. We eventually store this value as a const int fillSyle. Depending on the shapeType and fillStyle we may apply different algorithm accordingly later on.

        // ask for the Shape Fill Style
        int inputFillStyle;
        for (int keepAsking = 1; keepAsking == 1; )
        {
            cout << "Select Fill Style." << endl;
            cout << "  (1) - Hollow " << endl;
            cout << "  (2) - Solid " << endl;
            cout << "Select Fill Style (enter 1/2): ";
            cin >> inputFillStyle;

            // Make sure the user input is within range. If not, ask again!
            if (inputFillStyle == 1 || inputFillStyle == 2)
                {keepAsking = 0;}
        }
        const int fillStyle = inputFillStyle;

5 – Apply Algorithms

Algo Loop

All algorithms follow the same “looping” procedure. We have two loops (like in the previous exercises):

  • a for loop with the invariant r (increment of 1), and an asymmetric range of [0, numRows). Denote this as r[0,numRows].
  • a nested while loop with the invariant c, and an asymmetric range of [0, numCols). Denote this as c[0,numCols].

Just to refresh memory, asymmetric range of [0,numRows) means the range is betwen 0 and numRows, and excludeding numRows from the range. The looping procedure looks like this:

// invariant: we have written r rows so far
for ( int r = 0; r != numRows; ++r )
{
    string::size_type c = 0;

    // invariant: we have written c characters so far in the current row
    while ( c != numCols)
    {       

        // Algorithms go here (where the invariant c gets incremented accordingly)

     }
 }      

Rectangle (and Square) Algorithm

Algo Rectangle

If rectangle and square, use the solid/hollow rectangle algorithm (as square is a form of rectangle).

                // If Square or Rectangle Option
                if (shapeType == 1 or shapeType == 2)
                {
                    // hollow option
                    if (fillStyle == 1)
                    {
                        // Are we at the exact position to output asterisk (border)?
                        if (
                               ( c == 0 )                    // the left edge
                            || ( c == numCols-1)        // the right edge
                            || ( r == 0 )                     // the top edge
                            || ( r == numRows-1)       // or the bottom edge
                           )
                        { cout << "*"; ++c; }
                        else
                        { cout << ' '; ++c; }
                    }

                    // If solid option
                    else if (fillStyle == 2)
                    { cout << "*"; ++c; } 
                }

Triangle Algorithm

Algo Triangles

If triangle, use the solid/hollow triangle algorithm.

                // If Triangle Option
                if (shapeType == 3)
                {
                     // If Triangle hollow option
                    if (fillStyle == 1)
                    {
                        // Are we at the exact position to output asterisk (border)?
                        if (
                               ( c == ((numCols-1)/2)-r )  // the triangle left edge
                            || ( c == ((numCols-1)/2)+r )  // or the triangle right edge
                            || ( r == numRows-1)           // or the triangle bottom edge
                           )
                        { cout << "*"; ++c; }
                        else
                        { cout << ' '; ++c; }
                    }

                    // If triangle solid option
                    else if (fillStyle == 2)
                    {
                        // Are we at the exact position to output asterisk (solid fill)?
                        if (
                               ( c >= ((numCols-1)/2)-r )  // between the triangle left edge
                            && ( c <= ((numCols-1)/2)+r )  // and the triangle right edge
                           )
                        { cout << "*"; ++c; }
                        else
                        { cout << ' '; ++c; }
                    }
                }

6 – Extras

To make the problem more robust I have also added the following features:

A banner at the top to make the console output prettier.

        cout << endl;
        cout << "*********************************" << endl;
        cout << "*** The Shape Making Program  ***" << endl;
        cout << "*********************************" << endl;
        cout << endl;

Display the height and width values for clarification.

        // display the shape specification
        cout << endl;
        cout << "Height = " << numRows << " rows." << endl;
        cout << "Width = " << numCols << " columns." << endl;
        cout << endl;

At the end of the program, offer the user an option to re-start the program (from the top) by entering the y button on the keyboard (whereas entering any other keys will cause the program to terminate peacefully). To do this, we wrap the entire main program with a while loop.

int main()
{

    string tryAgain = "y";
    while (tryAgain == "y")
    {

        // The core program components go here

        // at the end of the program we ask user if he would like to re-run program
        // depending on the user input then this program may either re-run or terminate
        cout << "Again? (y/n): ";
        cin >> tryAgain;
    }
    return 0;
}

}
[/code]

The Complete Program

Putting this all together, we now have our complete program.

#include <iostream>
#include <string>

// say what standard-library names we use
using std::cin; using std::endl;
using std::cout; using std::string;

int main()
{
    // at the end of the program we ask user if he would like to re-run program
    // depending on the user input then this program may either re-run or terminate
    string tryAgain = "y";
    while (tryAgain == "y")

    {
        cout << endl;
        cout << "*********************************" << endl;
        cout << "*** The Shape Making Program  ***" << endl;
        cout << "*********************************" << endl;
        cout << endl;

        // ask for the Shape Type
        int shapeType;
        for (int keepAsking = 1; keepAsking == 1; )
        {
            cout << "Select the Shape Type." << endl;
            cout << "  (1) - Square " << endl;
            cout << "  (2) - Rectangle " << endl;
            cout << "  (3) - Triangle " << endl;
            cout << "Shape Type (enter 1/2/3): ";
            cin >> shapeType;

            // Make sure the user input is within range. If not, ask again!
            if (shapeType == 1 || shapeType == 2 || shapeType == 3)
                {keepAsking = 0;}
        }

        // ask for the shape height
        cout << "Enter Shape Height: ";
        int shapeHeight;
        cin >> shapeHeight;

        // set the total number of rows to write (as a constant)
        int const numRows = shapeHeight;        

        // Only need to ask for a width if the shape is a a rectangle
        // For square and triangle, the width is computed instead.
        int shapeWidth;
        if (shapeType == 2)
        {
            // ask for the shape width
            cout << "Enter Shape Width: ";
            cin >> shapeWidth;
        }

        // compute the number of columns to write (as a constant) based on shape type
        int finalShapeWidth;
        if (shapeType == 1)
            {finalShapeWidth = shapeHeight; }    // Compute square width
        else if (shapeType == 2)
            {finalShapeWidth = shapeWidth; }     // Define rectangle width
        else
           {finalShapeWidth = (2*numRows)-1; }   // Compute triangle width
        const string::size_type numCols = finalShapeWidth; // define constant here      


        // ask for the Shape Fill Style
        int fillStyle;
        for (int keepAsking = 1; keepAsking == 1; )
        {
            cout << "Select Fill Style." << endl;
            cout << "  (1) - Hollow " << endl;
            cout << "  (2) - Solid " << endl;
            cout << "Select Fill Style (enter 1/2): ";
            cin >> fillStyle;

            // Make sure the user input is within range. If not, ask again!
            if (fillStyle == 1 || fillStyle == 2)
                {keepAsking = 0;}
        }

        // display the shape specification
        cout << endl;
        cout << "Height = " << numRows << " rows." << endl;
        cout << "Width = " << numCols << " columns." << endl;
        cout << endl;

        // invariant: we have written r rows so far
        for ( int r = 0; r != numRows; ++r )
        {
            string::size_type c = 0;

            // invariant: we have written c characters so far in the current row
            while ( c != numCols)
            {
                // If Square or Rectangle Option
                if (shapeType == 1 or shapeType == 2)
                {
                    // hollow option
                    if (fillStyle == 1)
                    {
                        // Are we at the exact position to output asterisk (border)?
                        if (
                               ( c == 0 )              // the left edge
                            || ( c == numCols-1)       // the right edge
                            || ( r == 0 )              // the top edge
                            || ( r == numRows-1)       // or the bottom edge
                           )
                        { cout << "*"; ++c; }
                        else
                        { cout << ' '; ++c; }
                    }

                    // If solid option
                    else if (fillStyle == 2)
                    { cout << "*"; ++c; }
                }

                // If Triangle Option
                if (shapeType == 3)
                {
                     // Triangle hollow option
                    if (fillStyle == 1)
                    {
                        // Are we at the exact position to output asterisk (border)?
                        if (
                               ( c == ((numCols-1)/2)-r )  // the triangle left edge
                            || ( c == ((numCols-1)/2)+r )  // or the triangle right edge
                            || ( r == numRows-1)           // or the triangle bottom edge
                           )
                        { cout << "*"; ++c; }
                        else
                        { cout << ' '; ++c; }
                    }

                    // If triangle solid option
                    else if (fillStyle == 2)
                    {
                        // Are we at the exact position to output asterisk (solid fill)?
                        if (
                               ( c >= ((numCols-1)/2)-r )  // between the triangle left edge
                            && ( c <= ((numCols-1)/2)+r )  // and the triangle right edge
                           )
                        { cout << "*"; ++c; }
                        else
                        { cout << ' '; ++c; }
                    }
                }
            }
            cout << endl;
        }

        // ask user if he would like to start the program again
        cout << "Again? (y/n): ";
        cin >> tryAgain;
    }
    return 0;
}

Result

*********************************
*** The Shape Making Program  ***
*********************************

Select the Shape Type.
  (1) - Square
  (2) - Rectangle
  (3) - Triangle
Shape Type (enter 1/2/3): 1
Enter Shape Height: 5
Select Fill Style.
  (1) - Hollow
  (2) - Solid
Select Fill Style (enter 1/2): 1

Height = 5 rows.
Width = 5 columns.

*****
*   *
*   *
*   *
*****
Again? (y/n): y

*********************************
*** The Shape Making Program  ***
*********************************

Select the Shape Type.
  (1) - Square
  (2) - Rectangle
  (3) - Triangle
Shape Type (enter 1/2/3): 1
Enter Shape Height: 5
Select Fill Style.
  (1) - Hollow
  (2) - Solid
Select Fill Style (enter 1/2): 2

Height = 5 rows.
Width = 5 columns.

*****
*****
*****
*****
*****
Again? (y/n): y

*********************************
*** The Shape Making Program  ***
*********************************

Select the Shape Type.
  (1) - Square
  (2) - Rectangle
  (3) - Triangle
Shape Type (enter 1/2/3): 2
Enter Shape Height: 5
Enter Shape Width: 10
Select Fill Style.
  (1) - Hollow
  (2) - Solid
Select Fill Style (enter 1/2): 1

Height = 5 rows.
Width = 10 columns.

**********
*        *
*        *
*        *
**********
Again? (y/n): y

*********************************
*** The Shape Making Program  ***
*********************************

Select the Shape Type.
  (1) - Square
  (2) - Rectangle
  (3) - Triangle
Shape Type (enter 1/2/3): 2
Enter Shape Height: 5
Enter Shape Width: 10
Select Fill Style.
  (1) - Hollow
  (2) - Solid
Select Fill Style (enter 1/2): 2

Height = 5 rows.
Width = 10 columns.

**********
**********
**********
**********
**********
Again? (y/n): y

*********************************
*** The Shape Making Program  ***
*********************************

Select the Shape Type.
  (1) - Square
  (2) - Rectangle
  (3) - Triangle
Shape Type (enter 1/2/3): 3
Enter Shape Height: 5
Select Fill Style.
  (1) - Hollow
  (2) - Solid
Select Fill Style (enter 1/2): 1

Height = 5 rows.
Width = 9 columns.

    *
   * *
  *   *
 *     *
*********
Again? (y/n): y

*********************************
*** The Shape Making Program  ***
*********************************

Select the Shape Type.
  (1) - Square
  (2) - Rectangle
  (3) - Triangle
Shape Type (enter 1/2/3): 3
Enter Shape Height: 5
Select Fill Style.
  (1) - Hollow
  (2) - Solid
Select Fill Style (enter 1/2): 2

Height = 5 rows.
Width = 9 columns.

    *
   ***
  *****
 *******
*********
Again? (y/n): n

Process returned 0 (0x0)   execution time : 245.384 s
Press any key to continue.

Remarks

This (procedural) program works fine when the problem is fairly simple. When things get more complicated (e.g. if we are asked to produce hundreds or even thousands of different types of shapes, a procedural code might become harder and harder to write and maintain. In that case, I wonder if an object-orientated approach may help simplifying this? Something to come back to in future I guess. i.e. to rewrite the above program in an object oriented manner.

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-4

Exercise 2-4

The framing program (Exercise 2-0) writes the mostly blank lines that separate the borders from the greeting one character at a time. Change the program so that it writes all the spaces needed in a single output expression.

Solution

The key to solving this problem is to use the string constructor statement that we learnt from chapter 1 on string.

std::string z(n, c) ;

This statements defines z as a variable of types std::string that initially contains n copies of the character c. Here, c must be a char, not a string or a string literal.

For example, the statement std::string z(10, ‘ ‘); will create a string variable z that is occupied of 10 space characters (char). We define char values using single quotes. (double quotes are for defining string values).

Bearing this in mind, I now demonstrate the high level Solution Strategy.

Solution Strategy ACPP Solution 2-4
Solution Strategy

The program will need to do the followings:

  • Ask the user for his first name so we can build the constant string greeting.
  • Ask the user for the padding value so we can (in conjunction with the readily build greeting string) compute the total frame rows and columns. We will also be able to build the constant string padStringLeftRight and padStringTopBottom.
  • Create an asymmetric for loop 1 r[0,rows) – the invariant r tells us which row we are at.
  • At the beginning of each row, reset the invariant c to 0 – i.e. always start from column 0 (the first column of the row).
  • Create an asymmetric while loop 2 c[0,cols) within loop 1 – the invariant c tells us which column we are at, keeping the row constant.
  • We perform the following checks during each complete loop to determine what to output:
    1. Are we at the exact position to output the greeting string? If so, output the constant string greeting, and increment the invariant c by size of the string. Else…
    2. Are we on any border? if so, output a string literal of 1 asterisk character, and increment the invariant c by 1. Else…
    3. Are we on the greeting line? if so, output the constant string padStringLeftRight, and increment the invariant c by size of the string. Else…
    4. Output the constant string padStringTopBottom, and increment the invariant c by size of the string.

Notes:

  • We use a for loop 1 (instead of while loop) because the increment of the invariant r (for rows) s always by 1 – using a for loop is easier and clearer. Nevertheless if we want it is also okay to use a while loop – just a matter of preference really.
  • We use a while loop 2 (instead of a for loop) because the increment of the invariant c (for columns) changes depending on what we output. Sometimes the increment is 1 (asterisk), sometimes the size of the string (greeting, padStringLeftRight, padStringTopBottom).

Putting this all together, we now have our complete program.

#include <iostream>
#include <string>

// say what standard-library names we use
using std::cin;
using std::endl;
using std::cout;
using std::string;

int main()
{
    // ask for the person's name
    cout << "Please enter your first name: ";

    // read the name
    string name = "";
    cin >> name;

    // ask user to provide the padding between frame border and greeting message
    cout << "Please specify an integer padding value: ";
    int inPad = 0;
    cin >> inPad;

    // build the message that we intend to write
    const string greeting = "Hello, " + name + "!";

    // build the constant padding parameters
    const int pad = inPad; // padding

    // compute the number of rows to write
    const int rows = ( pad * 2 ) + 3;

    // compute the number of columns to write
    const string::size_type cols = greeting.size() + ( pad * 2 ) + 2;

    // build the padding string that lives on the same row as the greeting, between the greeting and the border.
    const string padStringLeftRight( pad, ' ' );

    // build the padding string that lives between the top (or bottom) border and and greeting.
    const string padStringTopBottom( ( cols - 2 ) , ' ' );

    // write a blank line to separate the output from the input
    cout << endl;

    // write 'rows' rows of output
    // invariant: we have written r rows so far
    for ( int r = 0; r != rows ; ++r )
    {
        string::size_type c = 0;

        // invariant: we have written c characters so far in the current row
        while ( c != cols)
        {
            // Are we at the exact position where the greeting should start?
            if ( ( r == pad + 1 ) && ( c == pad + 1 ) )
            {
                // output the greeting message
                cout << greeting;
                c += greeting.size();
            }
            else
            {
                // are we on the border?
                if (
                       ( r == 0 )         // top row
                    || ( r == rows - 1 )  // or bottom row
                    || ( c == 0 )         // or left-most column
                    || ( c == cols - 1 )  // or right-most column
                   )
                {
                    // output the border asterisks
                    cout <<  "*";
                    ++c;
                }
                else
                {
                    // are we on the greeting line?
                    if ( r == pad + 1)
                    {
                        // output the empty spaces between left (or right) border and the greeting
                        cout << padStringLeftRight;
                        c += padStringLeftRight.size();
                    }

                    else
                    {
                        // output the empty spaces between top (or bottom) border and the greeting
                        cout << padStringTopBottom;
                        c += padStringTopBottom.size();
                    }
                }
            }
        }
        cout << endl;
    }
    return 0;
}

Further explanations

Just little bit more explanation, just for peace of mind.

To compute the total rows required by the frame, we sum the:

  • top and bottom padding (pad * 2),
  • top border, bottom border, and the greeting lines (1 + 1 + 1 = 3).

Hence we have this line in the code:

    // compute the number of rows to write
    const int rows = ( pad * 2 ) + 3;

To compute the total columns required by the frame, we sum the:

  • The size of the greeting message ( greeting.size() )
  • The size of the left and right paddings combined ( pad * 2 )
  • The size of left and right borders ( 1 + 1 = 2 ),

Also, as per the book chapter 2.4 (page 22), it is good habit to use the type string::size_type (instead of integer) when computing the sum of string sizes.

Hence we have this line in the code:

    // compute the number of columns to write
    const string::size_type cols = greeting.size() + ( pad * 2 ) + 2;

Result

The program compiles okay. Here is our output.

Please enter your first name: Johnny
Please specify an integer padding value: 3

**********************
*                    *
*                    *
*                    *
*   Hello, Johnny!   *
*                    *
*                    *
*                    *
**********************

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-3

Exercise 2-3

Rewrite the framing program (Exercise 2-0) to ask the user to supply the amount of spacing to leave between the frame and the greeting.

Solution

Turns out that in my solution to Exercise 2-2 I already have covered this. i.e. to ask user to supply the horizontal and vertical padding separately. Please see my solution to Exercise 2-2 for details.

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-2

Exercise 2-2

Change the framing program (Exercise 2-0) so that it uses a different amount of space to separate the sides from the greeting than it uses to separate the top and bottom borders from the greeting.

Solution

The original framing program in Exercise 2-0 “hard-codes” the const int pad which forces both vertical and horizontal paddings to be identical. The question asks for a capability to assign the vertical padding and horizontal padding separately.

To do this I am going to adjust the original framing program taking into account of the followings:

  • Ask the user to provide the vertical padding, and store it as an int variable inPadY.
  • Ask the user to provide the horizontal padding, and store it as an int variable inPadX.
  • Assign the int variable inPadY to a constant integer padY.
  • Assign the int variable inPadX to a constant integer padX.
  • Replace all the (old) pad references associating with rows with padY (e.g. the constant rows and invariant r). Because the number of rows depends on vertical padding.
  • Replace all the (old) pad references associating with columns with padX (e.g. the constant cols and invariant c). Because the number of columns depends on horizontal padding.

The modified program looks like this:

#include <iostream>
#include <string>

// say what standard-library names we use
using std::cin;
using std::endl;
using std::cout;
using std::string;

int main()
{
    // ask for the person's name
    cout << "Please enter your first name: ";

    // read the name
    string name;
    cin >> name;

    // ask user to provide the vertical padding
    cout << "Please provide integer padY (vertical padding): ";
    int inPadY;
    cin >> inPadY;

    // ask user to provide the horizontal padding
    cout << "Please provide integer padX (horizontal padding): ";
    int inPadX;
    cin >> inPadX;

    // build the message that we intend to write
    const string greeting = "Hello, " + name + "!";

    // build the constant padding parameters
    const int padY = inPadY; // vertical padding
    const int padX = inPadX; // horizontal padding

    // the number of rows to write
    const int rows = ( padY * 2 ) + 3;

    // the number of columns to write
    const string::size_type cols = greeting.size() + ( padX * 2 ) + 2;

    // write a blank line to separate the output from the input
    cout << endl;

    // write 'rows' rows of output
    // invariant: we have written r rows so far
    for ( int r = 0; r != rows ; ++r )
    {
        string::size_type c = 0;

        // invariant: we have written c characters so far in the current row
        while ( c != cols)
        {
            // is it time to write the greeting?
            if ( ( r == padY+ 1 ) && ( c == padX + 1 ) )
            {
                cout << greeting;
                c += greeting.size();
            }
            else
            {
                // are we on the border?
                if (
                       ( r == 0 )         // top row
                    || ( r == rows - 1 )  // or bottom row
                    || ( c == 0 )         // or left-most column
                    || ( c == cols - 1 )  // or right-most column
                   )
                {
                    cout <<  "*";
                }
                else
                {
                    cout << " ";
                }
                ++c;
            }
        }
        cout << endl;
    }
    return 0;
}

Result

Case 1: padY = 1; padX = 10

Please enter your first name: Johnny
Please provide integer padY (vertical padding): 1
Please provide integer padX (horizontal padding): 10

************************************
*                                  *
*          Hello, Johnny!          *
*                                  *
************************************

Case 2: padY = 5; pad X = 5

Please enter your first name: Johnny
Please provide integer padY (vertical padding): 1
Please provide integer padX (horizontal padding): 10

************************************
*                                  *
*          Hello, Johnny!          *
*                                  *
************************************

Case 3: padY = 10 padX = 1

Please enter your first name: Johnny
Please provide integer padY (vertical padding): 5
Please provide integer padX (horizontal padding): 5

**************************
*                        *
*                        *
*                        *
*                        *
*                        *
*     Hello, Johnny!     *
*                        *
*                        *
*                        *
*                        *
*                        *
**************************

So far, so good!

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-1

Exercise 2-1

Change the framing program (Exercise 2-0) so that it writes its greeting with no separation from the frame.

Solution

The sort of output that we are after looks like this:

</p>

<hr />

<p><em>Hello, Johnny!</em></p>

<hr />

<p>

i.e. We can just simply re-use the same program that we wrote in Exercise 2-0, except this time we change the padding of 1 (space character) to 0 (space characters). In other word, no padding round the greeting message.

For completeness sake, I have copied and pasted the code from Exercise 2-0 here. Note that this time I change the const int pad value to 0. (See line 23 below!)

</p>

<h1>include &lt;iostream&gt;</h1>

<h1>include &lt;string&gt;</h1>

<p>// say what standard-library names we use
using std::cin;
using std::endl;
using std::cout;
using std::string;</p>

<p>int main()
{
    // ask for the person's name
    cout &lt;&lt; &quot;Please enter your first name: &quot;;</p>

<pre><code>// read the name
string name;
cin &amp;gt;&amp;gt; name;

// build the message that we intend to write
const string greeting = &amp;quot;Hello, &amp;quot; + name + &amp;quot;!&amp;quot;;

// the number of blanks surrounding the greeting
const int pad = 0; // no padding this time

// the number of rows and columns to write
const int rows = ( pad * 2 ) + 3;
const string::size_type cols = greeting.size() + ( pad * 2 ) + 2;

// write a blank line to separate the output from the input
cout &amp;lt;&amp;lt; endl;

// write 'rows' rows of output
// invariant: we have written r rows so far
for ( int r = 0; r != rows ; ++r )
{
    string::size_type c = 0;

    // invariant: we have written c characters so far in the current row
    while ( c != cols)
    {
        // is it time to write the greeting?
        if ( ( r == pad + 1 ) &amp;amp;&amp;amp; ( c == pad + 1 ) )
        {
            cout &amp;lt;&amp;lt; greeting;
            c += greeting.size();
        }
        else
        {
            // are we on the border?
            if (
                   ( r == 0 )         // top row
                || ( r == rows - 1 )  // or bottom row
                || ( c == 0 )         // or left-most column
                || ( c == cols - 1 )  // or right-most column
               )
            {
                cout &amp;lt;&amp;lt;  &amp;quot;*&amp;quot;;
            }
            else
            {
                cout &amp;lt;&amp;lt; &amp;quot; &amp;quot;;
            }
            ++c;
        }
    }
    cout &amp;lt;&amp;lt; endl;
}
return 0;
</code></pre>

<p>}</p>

<p>

Result

The program compiles successfully and produce the following output in the standard console output window.

Please enter your first name: Johnny</p>

<hr />

<p><em>Hello, Johnny!</em></p>

<hr />

<p>Process returned 0 (0x0)   execution time : 2.905 s
Press any key to continue.

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 2-0

Exercise 2-0

Compile and run the program presented in this chapter.

Solution

Note: The comprehensive program and explanation are documented very clearly in chapter 2 of the book. Refer to the chapter for details.

This is what the program does:

  • Ask user for his first name.
  • Display a framed greeting message that looks like this:
******************
*                *
* Hello, Johnny! *
*                *
******************

Specification as follows:

  • The greeting message is in the middle of the frame.
  • We add a padding of 1 (space character) all the way around the greeting message.
  • We display the frame with asterisk (*).

This is the full code as described in the chapter. I have added some extra brackets for clarity purpose.

#include <iostream>
#include <string>

// say what standard-library names we use
using std::cin;
using std::endl;
using std::cout;
using std::string;

int main()
{
    // ask for the person's name
    cout << "Please enter your first name: ";

    // read the name
    string name;
    cin >> name;

    // build the message that we intend to write
    const string greeting = "Hello, " + name + "!";

    // the number of blanks surrounding the greeting
    const int pad = 1;

    // the number of rows and columns to write
    const int rows = ( pad * 2 ) + 3;
    const string::size_type cols = greeting.size() + ( pad * 2 ) + 2;

    // write a blank line to separate the output from the input
    cout << endl;

    // write 'rows' rows of output
    // invariant: we have written r rows so far
    for ( int r = 0; r != rows ; ++r )
    {
        string::size_type c = 0;

        // invariant: we have written c characters so far in the current row
        while ( c != cols)
        {
            // is it time to write the greeting?
            if ( ( r == pad + 1 ) && ( c == pad + 1 ) )
            {
                cout << greeting;
                c += greeting.size();
            }
            else
            {
                // are we on the border?
                if (
                       ( r == 0 )         // top row
                    || ( r == rows - 1 )  // or bottom row
                    || ( c == 0 )         // or left-most column
                    || ( c == cols - 1 )  // or right-most column
                   )
                {
                    cout <<  "*";
                }
                else
                {
                    cout << " ";
                }
                ++c;
            }
        }
        cout << endl;
    }
    return 0;
}

Result

The program compiles successfully and produce the following output in the standard console output window.

Please enter your first name: Johnny

******************
*                *
* Hello, Johnny! *
*                *
******************

Process returned 0 (0x0)   execution time : 2.699 s
Press any key to continue.

Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000

Accelerated C++ Solution to Exercise 1-6

Exercise 1-6

What does the following program do it, when it asks you for input, you type two names (for example, Samuel Beckett)? Predict the behaviour before running the program, then try it.

#include <iostream>
#include <string>

int main()
{
    std::cout << "What is your name? ";
    std::string name;
    std::cin >> name; // frist 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;

}

Solution

At the first  std::cin phase we type in two words, “Samuel Beckett”, followed by hitting the enter key. The following happens:

(1) the two words initially get stored in the buffer (corresponding to std::string variable name)

Samuel Beckett

Note that the buffer automatically discard the leading and trailing blanks. Each word is separated by a space character.

(2) the std::cin cuases the buffer the flush out the first word “Samuel” to the std::string variable assignment. The std::string name has now a value of “Samuel”.

Because the first word has now been flushed out from the buffer, the buffer for name now looks like this:

Beckett

Note that the first word Samuel is now gone. It has been flushed away from the buffer.

At the second std::cin phase, becuase there is already a word in the buffer, it just simply asks the buffer to flush out that value and assign that to the std::string variable name. The std::string name has now a value of “Beckett” (and replace the old value “Samuel”)

Because the first word has now been flushed out from the buffer, the buffer for name now looks like this:


The buffer s now empty!

If there turn out to be a third std::cin step, the user will be asked to supply some values to the buffer, so that the std::cin facility may read from it. (But in this case, the program only has two std::cin steps).

Summary

In summary, the std::cin checks the buffer corresponding to the std::string variable.

  • If there is at least one word stored inside the buffer, it flushes the first word from the buffer, read and assign that word to the std::string variable. The program does not need to pause and ask the user to supply values.
  • If the buffer is empty, the program would pause and ask the user to supply values (words) to the buffer. Only when there are words inside the buffer, the std::cin would then read from it and assign the value to the std::string variable (and flush that word away from the buffer as a consequence).

Comments

This question appears to be quite simple at first sight, and after going through such detail investigation, it has revealed so much “hidden scene” behind the std::cin and buffer operations. The above summarises my own humble understanding – this is the most logical visualisation I could come up with. (and I hope it is correct!)

For completeness, let us run the program and confirm our understanding.

What is your name? Samuel Beckett
 Hello, Samuel
And what is yours?Hello, Beckett; nice to meet you too!

Process returned 0 (0x0)   execution time : 21.496 s
Press any key to continue.

Further Experiments

To confirm the hypothesis stated in the summary section above, let’s do three more experiments. Knowing that there are two std::cin steps in the program, we can try the followings:

  • Experiment 1: At the first std::cin phase, provide 0 words and hit enter.
  • Experiment 2: At the first std::cin phase, provide 1 word and hit enter.
  • Experiment 3: At the first std::cin phase, provide 3 words and hit enter.

Results to Experiments

Experiment 1

At the first std::cin phase, I provide 0 words and hit enter.

Result to Experiment 1

Because the buffer for the std::string variable is empty, hitting the enter button does not get us anywhere – the program insists us to provide at least 1 word to the buffer.

Experiment 2

At the first std::cin phase, I provide 1 word and hit enter.

Result to Experiment 2

This satisfies the first std::cin phase – it reads the word that I provide and use it for the first std::cout phase. Buffer is empty as a consequence. When it reaches the second std::cin phase however, because the buffer is empty it pauses and asks me to provide some words in order to proceed.

Result Experiment 3

At the first std::cin phase, I provide 3 words and hit enter.
Result:

  • The initial buffer contains 3 words.
  • The first std::cin flushes first word from the buffer. The buffer contains the remaining 2 words as a consequence.
  • The following std::cin flushes first word from the buffer. The buffer contains the remaining 1 word as a consequence.

(note: we the program contains a 3rd std::cin step, it would just read that remaining 1 word from the buffer).

 Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000