Jump to content

C++ Programming/Lost Bits

From Wikibooks, open books for an open world

Streams

[edit | edit source]

"safe bool" idiom

[edit | edit source]

or Why define an operator void*() cast operator rather than an operator bool()?

This is so that we cannot write things like:

int foo = std::cin;

or, more importantly,

int bah;
std::cin << bah;    // observe: << instead of >>

by mistake. However, it is not perfect, as it allows other mistakes such as

delete std::cin;

though fortunately such errors are less likely, as delete should be used carefully in any case.

The state of the art would have us instead define a private nested class dummy within std::ios, and return a pointer-to-member-function of dummy -- hence allowing implicit conversion from that to bool, but not allowing many other operations. This is sometimes referred to as the "safe bool" idiom, and is motivated by the fact that C++'s bool type has implicit conversions both to and from int as a result of the standardization process.


Clipboard

To do:
"safe bool" idiom can probably be more clear or have linking to other relevant information if available.


The auto keyword used to have a different behavior, but in C++ it allows one to omit the type of a variable and let the compiler decide. This is particularly useful for generic programming in which the return type of a function may depend on the type of its arguments. Thus, rather than this:

int x = 42;
std::vector<double> numbers;
numbers.push_back(1.0);
numbers.push_back(2.0);
for(std::vector<double>::iterator i = numbers.begin();
    i != numbers.end(); ++i) {
  cout << *i << " ";
}

we could write this:

auto x = 42; // We can use auto on base types...
std::vector<double> numbers;
numbers.push_back(1.0);
numbers.push_back(2.0);
// But auto is most useful for complicated types.
for(auto i = numbers.begin(); i != numbers.end(); ++i) {
  cout << *i << " ";
}

The concept of scope is straightforward unless procedures are included in the mix; then it becomes more difficult to follow:

// Confusing Scope Program
#include <iostream>

using namespace std;

int i = 5;           /* The first version of the variable 'i' is now in scope */

void p(){
  int i = -1;        /* A ''new'' variable, also called 'i' has come into existence. The 'i' declared above is now out of scope,*/
  i = i + 1;
  cout << i << ' ';  /* so this ''local'' 'i' will print out the value 0.*/
}                    /* The newest variable 'i' is now out of scope. The first variable, also called 'i', is in scope again now.*/

int main(){
  cout << i << ' ';  /* The first variable 'i' is still in scope here so a ''5'' will be output here.*/
  char ch;
  int i = 6;         /* A ''new'' variable, also called 'i' has come into existence. The first variable 'i' is now out of scope again,*/
  i = i + 1;
  p();
  cout << i << endl; /* so this line will print out a ''7''.*/
  return 0;
}                    /* End of program: all variables are, of course, now out of scope.*/

The first variable 'i' is put out of scope in two separate sections. Thus the repeated statement cout << i << ' '; means something very different each time it is written. The 'i' referred to is a different location in memory on each occasion. This is what was meant by `context' in the introduction: the context or background in which the statement is placed of is different each time, so the statement does something different in each place.

There are important things to note about the above example. The program is only an example and unusually convoluted, it is useful to demonstrate the idea of scope and little else. While it illustrates the concept of scope, it fails to illustrate usefully the purpose of scope.

The purpose of Scope

[edit | edit source]

Some variables are required to store information for an entire program, while other variables are short-term variables which are brought into existence momentarily for a single small purpose and then disposed of, by going out of scope. In the following program, an array of numbers is read in and then a procedure is called that computes the average of the numbers in the array. Within the procedure, in order to move through the array and select the elements in the array in turn, a variable 'i' is created for that one purpose. Contrast the two kinds of variable: the array itself, which is in scope throughout the entire program, and the variable 'i' which is in scope only for a small section of the code, to do its own little job.

// Program Average
#include <iostream>

using namespace std;

float a[10];                      /* a is now in scope.*/
int length;                       /* length is now in scope.*/

float average(){
  float result = 0.0;             /* result is now in scope.*/

  for(int i = 0; i < length; i++){ /* i is now in scope.*/
    result += a[i];
  }                               /* i is now out of scope.*/

  return result/length;
}                                 /* result is now out of scope.*/

int main(){
  length = 0;
  cout << "enter a number for each of the next 10 lines" << endl;

  while( length != 10 )
    { cin >> a[length++]; }

  float av = average();            /* av is now in scope.*/
  cout << endl << "average: " << av << endl;

  return 0;
}                                 /* All variables now out of scope.*/

Scope and control structures

[edit | edit source]

Within a single procedure it is possible to begin a new level of scoping. In fact it occurs every time a left brace `{' is written and it ends where its matching right brace '}' is written. Thus layers of scope to any depth can be built up as can be seen in the following program. The following program has four levels of scoping. The innermost level is said to be enclosed by the levels around it, thus we talk about inner levels and enclosing levels of scoping. Again this program is for illustration purposes only and does nothing of real value.

// Complicated Scope Program
#include <iostream>
  
using namespace std;  /* outermost level of scope starts here */

int i;

int main(){               /* next level of scope starts here */
  int i;
  i = 5;

  {                   /* next level of scope starts here */
    int j,i;
    j = 1;
    i = 0;
  
    {                 /* innermost level of scope of this program starts here */
      int k, i;
      i = -1;
      j = 6;
      k = 2;
    }                 /* innermost level of scope of this program ends here */

    cout << j << ' ';
  }                   /* next level of scope ends here */

  cout << i << endl;
  return 0;
}                     /* next and outermost levels of scope end here */

The output if this program is 6 5. To understand why, we look first at the simpler situation of 'i', and we see why a five is printed for it, and then next at the more complicated situation with 'j' and why a six is printed for it.

A new variable 'i' is created at each new level of scope. Thus only the first assignment to 'i', where 'i' is assigned the value of five, affects this particular variable 'i': the variable that is printed. That variable does not alter its value after that first assignment and so the final statement prints a five. The other assignments to 'i' are irrelevant to the final print statement.

In contrast, the variable 'j' is created only once, hence the assignment where `j' is assigned the value of six, alters this only existing variable called 'j' even though the variable is declared at an enclosing level of scope. If a program has a scope level inside another, and no variable of the right name is declared at this inner level, then the computer `looks outwards' to the next enclosing level of scope. If there is no variable of that name declared there either, then it will look out further and again further outwards until the variable's declaration is found. (Of course, if a declaration for the variable is never found then the compiler indicates an error, stating that the variable is undeclared and so the program is not compiled.)

Scope using other control structures

[edit | edit source]

Above we stated that a new level of scope started with each left brace `{'. However, it is not common to use just a naked left brace: usually the left brace is associated with an if statement or a while statement or some such. We add these to the above program to make the more usual (but still useless) example program following. The program does compile and it prints a 5 when it runs, but does little of value as a program.

// Complicated Scope Program, variation 1
#include <iostream>

using namespace std;  /* outermost level of scope starts here */

int i;

int main(){               /* next level of scope starts here */
  int i;
  i = 5;

  while(i != 5) {     /* next level of scope starts here */
    int j,i;
    j = 1;
    i = 0;
    switch (i) {      /* next level of scope starts here */
      int i;

      case 1:
        if (i != 4) { /* innermost level of scope of this program starts here */
          int k, i;
          i = -1;
          j = 6;
          k = 2;
        }             /* innermost level of scope of this program ends here */
        break;

      case 2: 
        j = 5;
        break;
    }                 /* next level of scope ends here */

    cout << j << ' ';
  }                   /* next level of scope ends here */

  cout << i << endl;
  return 0;
}                     /* next and outermost levels of scope end here */

We added an extra level of scoping at the switch statement because this statement demands a left brace, and it shows that even at that point a new level of scope is opened (because we were able to declare yet another variable called `i' without getting an error). As you can see, the various control structures (i.e. the while, if and switch statements) all end at their own matching right brace and hence at the same point as the scope ends. It is fair to say that each of these control statements operates over an area of scope. But remember, scope is a concept about variable names and the area in which they are defined, not control structures.

It is not essential that either the if or the while statements have an opening brace following, and program average above shows an example of this with its while statement. In this situation, no new level of scope is begun. It is the left brace that opens the new level of scope, not the if or while statement itself. The switch statement demands a left brace at that point, but notice that the `i' switch variable is at the old level of scope. The new level of scope comes into existence immediately following the left brace as always.

For all practical purposes, the for loop control structure can be seen to have scoping that operates in the same manner. However it is permissible to declare the for loop variable within the for statement itself, as can be seen in line eight of program average above. This is good programming practice as it makes the structure of the program clearer.

With all the programs in this section the statements start further and further to the right as the level of scope deepens. This is referred to as indentation and is a very important feature of program presentation. It makes the scope level explicit at all times and also makes it clear where a statement ends. For example, in the above program the line cout << j << ' '; is within the while loop whereas cout << i << endl; is outside that loop. The loop and the scope level end at the same point: the right brace between these two statements signals the end of both of these.

The scoping of the for control statement in detail

[edit | edit source]

This subsection is probably more confusing than useful, but it is offered for completeness; feel free to skip it.

The for control statement has an unusual scoping, in that the left round bracket also starts its own level of scope. Thus the following program is legal:

// Complicated Scope Program, variation 2
#include <iostream>
  
using namespace std; /* outermost level of scope starts here */

int i;

int main(){              /* next level of scope starts here */
  int i;
  i = 5;

  for(               /* next level of scope starts here */
      int i = 1;
      i<10 && cout << i << ' ';
      ++i )  
  {                  /* next level of scope starts here */
    int i = -1;
    cout << i << ' ';
  }                  /* two levels of scope end here*/

  cout << i << endl;
  return 0;
}                    /* next and outermost levels of scope end here */

and it gives the output:

 1 -1 2 -1 3 -1 4 -1 5 -1 6 -1 7 -1 8 -1 9 -1 5

This special feature of the for statement is not shared by, for example, the while statement. It is a syntax error to attempt to declare a variable within the while statement, so while(int i < 22)i++; gives a syntax error.

This special scope level enables one to declare a for loop variable within the for loop itself (like the variable 'i' in the above program), instead of having to declare it in the enclosing scope level, creating a neater program. But it is a little peculiar.

The above program does show one interesting feature: in order to check if a new level of scope has been opened, it is only necessary to attempt to declare an existing variable again at that level, as was done with the variable `i' in the above example program, which has a new variable `i' declared at every level possible.

The above program also illustrates one further very important point: there is a saying in computer programming that is it possible to write bad code in any language. The above program is quite unclear in its operation and a good example of bad coding: it is good code to illustrate a point, but bad code to read. All code should be written to make the program as clear as possible as discussed elsewhere under the topic of program style.

Scope and lifetime

[edit | edit source]

The scope of a variable should be contrasted with its lifetime. In the program above called `Confusing Scope Program' the first variable `i' goes out of scope for a time but it remains in existence, thus its lifetime is continuing while it is out of scope. In older programming languages, it is difficult to contrive examples where scope and lifetime are different - in general in these older languages the two are the same, so lifetime equals scope. Not only is it difficult, it is not all that useful in the general case when it does occur. In recently created computer languages like C++ however, the idea of having variables that are out of scope but still alive is heavily exploited and creates the principle distinguishing feature of C++: the C++ class. This is the above program re-written with a class:

// Program Average rewritten using a class
#include <iostream>

using namespace std;

class StatisticsPackage{
private:
  float aa[20];                     /* aa scope start*/
  int length;                       /* length scope start*/
public:                           
  float average(){
    float result = 0.0;             /* result scope start*/
 
    for(int i = 0; i < length; ++i) /* i scope start*/
      result += aa[i];
 
  return result/length;
  }                                 /* result and i scope end*/

  void get_data(){
    length = 0;
    while(cin >> aa[length++]);
    --length;
  }
};                                  /* aa and length scope end*/

int main(){
  StatisticsPackage sp;             /* aa and length lifetimes start */
  sp.get_data();
  float av = sp.average();          /* av scope start*/
  cout << av << endl;
  return 0;
}                                   /* av scope end*/

In this version of the program, the variables `length' and `aa' are alive after the class `sp' comes into existence. However, their scope has been limited: they are alive but out of scope, storing the information but not being directly available in the main program. Keeping variables out of scope in this manner is very helpful in debugging a program, by narrowing the number of lines in which the variable in question can possibly change its value.


Clipboard

To do:
evolve and add references to the further insight and practical usefulness in sections like class space.


strcat_s

[edit | edit source]

strcat_s function proposed for close replacement of strcat function . strcat_s have an additional argument than strcat which specifies what will be the maximum size of destination string. strcat_sfunction appends characters of the source string to the end of first string destination string

Syntax
#include <string.h>
errno_t strcat_s(char *  dest,rsize_t s1max,const char * scr);

for example :

char str1 , str2;
printf( "Enter your first string : " );
scanf( "%s", str1 );
printf( "Enter your second string: " );
scanf( "%s", str2);
strcat_s(str1, 16, str2);  
printf( " %s\n", str1 );


Clipboard

To do:
It would be probably acceptable to include a link to the standard strcat() function, and make a special section regarding particularities of the Microsoft C Runtime Library and some other C libraries. At this time there is not enough content or displayed interest in doing so.