Category Archives: Programming

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

Accelerated C++ Solution to Exercise 1-5

Exercise 1-5

Part 1: Is this program valid? If so, what does it do? If not, say why not, and rewrite it to be valid.

#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;
}

Solution

No. The program is not valid and require correction.

Like the previous exercises (1-3 / 1-4), the key to this question is to understanding the term scope. One scope may not “see” what’s inside the other scope(s).

For clarity let me add some comments to the code to visualise these scopes.

// 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

} // scope main ends
[/code]

Let me start off by stating the following facts (in general)

  • All local variables defined at the outer scope level may be seen/used by the inner scopes (at all levels).
  • The reverse is not possible however. i.e. All local variables defined at the inner scope level may NOT be seen/used by the outer scopes (at all levels), nor the scopes adjacent to it. (i.e. same level scopes).
  • i.e. the permeation of variables go from outer scope, to inner scope. It does not permeate to other scopes at the same level, and/or the inner scopes (at all levels).

To apply these facts to our case:

  • scope main-1-1 can see std::string variable s (which is defined in the outer scope main-1), and its own defined std::string variable x.
  • scope main-1 can see only its own defined variable std::string s. It cannot see the std::string variable x that lives at the inner scope main-1-1 level. Problem! Fail to perform that std::cout << x << std::endl step because it does not not what x is. from scope main-1 perspective the variable x is not declared.
  • scope main-1 has no idea of any variables defined in the inner scopes main-1 nor main-1-1. All it knows is that whenever the implementation hits the return statement, it is done.

To prove the point if we run the program as it is, we expect to see a compilation error

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

To make the std::string x visible to the scope main-1, simply move the scope main-1-1 up 1 level by removing the curly braces. Like this:

// corrected - likely to work
#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

Running this corrected version program gives us the desirable result, as expected.

a string
a string, really

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

Reference

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

Accelerated C++ Solution to Exercise 1-4

Exercise 1-4

Part 1: Is the following program valid? if so what does it do? If not, why not?

Part 2: What if we add a semi-colon between the second-last and third-last (right) curly braces?

#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;
        }
    }

}

Solution (Part 1)

Yes. The program is valid.

Like the previous exercise (1-3), the key to this question is to understanding the term scope. Each pair of curly braces {} form a scope. It is okay to have scopes nested within a scope.

For clarity let me add some comments to the code to visualise these scopes.

#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

The const std::string variable s in scope main-1 is not the same const std::string variable s in scope main-1-1 (which is nested inside scope main-1). Even though scope main-1-1 is nested inside scope main-1, all local variables inside scope main-1-1 is hidden from view of main-1.

For completeness, let’s run the program at the top to confirm it runs okay.

a string
another string

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

The program runs okay as expected.

Memory Treatment

Another thing to bear in mind is the fact that all local variables within a scope only has a life time within the scope. i.e. once the implementation reaches the closing curly brace, }, (in other words, the end of the scope) the local variables of that scope are destroyed. The memory that was taken up by the local variables are now freed up and returned back to the system.

In this case, we expect to see the followings memory allocations / de-allocations.

  • When scope main starts: memory allocated for any local variables of scope main (in this case, none).
  • When scope main-1 starts: memory allocated for the std::string s variable of scope main-1.
  • When scope main-1-1 starts: memory allocated for the std::string s variable of scope main-1-1.
  • When scope main-1-1 ends: The std::string s variable of scope main-1-1 is destroyed. Memory is freed up and returned to the system.
  • When scope main-1 ends: The std::string s variable of scope main-1 is destroyed. Memory is freed up and returned to the system.
  • When scope main ends: Any scope main local variables are destroyed (in this case none). Memory is freed up and returned to the system. (in this case none)

Notice how this differs to the case in exercise 1.3?

Solution (Part 2)

Adding a semi-colon (;) between the second last and third last right curly braces will still constitute a valid program. For clarity, this is what the program would look like with that semi-colon.

#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

The additional semi-colon essentially creates a null-statement within the main-1 scope. i.e. it has no effect to the code. I think the reason the authors ask this question is to solidify our understanding on scope. i.e. which scope does that semi-colon belong to? Writing a C++ code in the above manner helps us visualise this easier.

Submitting the program with this semi-colon would yield the same output.

Reference

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

Accelerated C++ Solution to Exercise 1-3

Exercise 1-3

Is the following program valid? if so what does it do? If not, why not?

#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;
    }

}

Solution

Yes. The program is valid.

The key to this question is to understanding the term scope. Each pair of curly braces form a scope.

Within the main function (scope), we have two sub-scopes, as defined by the two pairs of curly braces. Visualise these as block 1 and block 2. Block 1 corresponds to line 6 to 9. Block 2 corresponds to line 10 to 13.

Each block constitute its own scope – all local variables and statements within each scope are independent to each other. The const std::string variable s in block 1 is not the same as the const std::string variable s in block 2.

Because of this, even though there is a const std::string variable s in block 1, it is okay to define another constd std::string variable s in block 2 – due to the fact the two variables are in different scope.

It is however not possible to re-define a const std::string variable s within the same block (scope). For instance, the following would not be valid:

    {
        const std::string s = "a string";
        const std::string s = "another string"; // this is not valid.
        std::cout << s << std::endl;
    }

For completeness, let’s run that program at the top to confirm it compiles and runs okay.

a string
another string

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

The program runs okay as expected.

Memory Treatment

Another thing to bear in mind is the fact that all local variables within a scope only has a life time within the scope. i.e. once the implementation reaches the closing curly brace, }, (in other words, the end of the scope) the local variables of that scope are destroyed. The memory that was taken up by the local variables are now freed up and returned back to the system.

In this case, as the two blocks of scopes are implemented in a sequential manner, we expect to see the followings memory allocations / de-allocations.

  • Line 7: memory allocated for the std::string s variable of scope block 1.
  • Line 9: The std::string s variable of scope block 1 is destroyed. Memory is freed up and returned to the system.
  • Line 11: memory allocated for the std::string s variable of scope block 2.
  • Line 13: The std::string s variable of scope block 2 is destroyed. Memory is freed up and returned to the system.

Reference

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

Accelerated C++ Solution to Exercise 1-2

Exercise 1-2

Are the following definitions valid? Why or why not?

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

Solution

No. The use of the concatenation operator is not valid. i.e. we bump into a “taboo” scenario: string literal + string literal. This is not valid.

The key in answering this question is to acknowledge the use of the string concatenation operator +.

  • It is left associative.
  • We can use + to concatenate a string and a string literal (and vice versa), or a string and a string, but not a string with a string literal (nor vice versa).

Line 1 defines a string variable exclam. This line is valid.

Line 2 defines a string variable message with the concatenation operator. The logic looks like this:

message = ( ( "Hello " + ", world" ) + exclam) 
        = ( ( a string literal + a string literal ) + a string )
        = ( ( compilation error! ) + a string)

i.e. We expect to encounter the invalid string literal + string literal scenario. This will likely cause compilation error.

Let’s test running the following program to prove the invalidity of the program.

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

Result

As expected, we get a compilation error.

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

The const char [6] corresponds to the string literal “Hello”.

The const char [8] corresponds to the string literal “, world”.

(Note: I recall from reading another C++ book that the length of a string is usually the number of character plus one – that plus one is for reserving a space for a backslash, \ , character at the end)

We can easily fix this by avoiding the string literal + string literal scenario. For instance, we may imply merge the two string literal together upfront. (Keep it simple and stupid!)

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

For completeness sake, let’s run the following corrected program – we should expect to avoid that compilation error.

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

Running this program produces the output as expected.

Hello, world!

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

Reference

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