Part F - Conclusion

The ISO/IEC Standard

Describe the evolution of C++
Describe old-style casts and their relation to type safety
Identify notable differences between C++ and C
Identify legacy issues in the evolution of C++

"C++11 feels like a new language: The pieces just fit together better than they used to and I find a higher-level style of programming more natural than before and as efficient as ever." (Stroustrup, 2013)

History | Casting | C++ versus C | Legacy Issues


International standards document the evolution of popular programming languages, including procedural and object-oriented ones.  The most recent standard for C++ is formally known as ISO/IEC 14882:2011 and available in a document containing over 1300 pages.  ISO stands for the International Organization for Standardization.  IEC stands for the International Electrotechnical Commission. 

This chapter reviews the milestones in the evolution of C++, summarizes the new features that have been introduced with respect to the original version of the language and describes some features that should either be avoided or no longer be used. 


History

C++ grew out of the C programming language, which had and still has no object-oriented capabilities.  The evolution of C is documented separately in the ISO/IEC 9899 set of standards, informally known as C89, C99 and C11. 

Bjarne Stroustrup developed C++ at Bell Labs (AT&T Research Labs) and released it officially in October 1985.  He called his 1979 experimental version C with Classes, but that name didn't stick.  The name C++ arose around 1983: the ++ stands for the C post-fix increment operator, which indicates that C++ is a true augmentation of C rather than a separate language.  Standard C++ and standard C are now considered siblings. 

Evolution of C++

The standards committees of C (ISO/IEC 9899) and C++ (ISO/IEC 14882) cooperate to keep C and C++ as closely related as possible without jeopardizing their different objectives. 

Stroustrup's web site includes a useful, up-to-date glossary of technical terms.

Standard C++

C++98

The first official standard that defined the C++ language is formally known as ISO/IEC 14882:1998 and less formally as C++98.  This standard was internationally ratified in 1998 and published in a document that contains about 800 pages.  C++98 was based on the ISO/IEC 9899:1990 standard for the C language (less formally known as C89). 

In addition to the facilities provided by C89, C++98 provided

"additional data types, classes, templates, exceptions, namespaces, inline functions, operator overloading, function name overloading, references, freestore management operators, and additional library facilities."

These facilities included the iostream library, the string class, function templates, inline functions, all of which have been described in these notes. 

C++11

The official standard that now defines the C++ language is formally known as ISO/IEC 14882:2011 and less formally as C++11.  This standard was internationally ratified on August 12 2011.  It is based on C++98 and C99 and includes a few additions to the core language as well as several major extensions to its standard library.  This is the version of the language described in these notes. 

The aims of the C++11 effort included:

  • making C++ easier to teach and to learn through increased uniformity
  • making C++11 better for systems programming and library construction
  • improving the type safety of the language

The new features in C++11 included:

  • the nullptr keyword to replace NULL
  • the auto keyword to infer the type of a left operand implicitly from the type of the right operand in an assignment expression
  • inherited constructors
  • uniformity amongst initializers
  • initializers for class members

Compiler Support Status

The level of support for C++11 on different compilers is documented at http://wiki.apache.org/stdcxx/C++0xCompilerSupport.

Many features have been incorporated into version 17 of Microsoft's command line compiler.  The compiler supports Visual Studio 2012 and has the features enabled by default. 

Many features have also been incorporated into version 4.7 of the GNU compiler.  That compiler supports g++.  The features can be enabled on the command line by specifying the std option:

 g++ -std=c++11 source.cpp
 g++ -std=c++0x source.cpp

c++0x refers to the working draft that led up to C++11 and is available with version 4.6.  The current state of support for the GNU compiler is documented at http://gcc.gnu.org/projects/cxx0x.html.

C++14

The ISO/IEC 14882 committee is now working on the next iteration scheduled for around 2014.  This revision intended to be a minor amendment to the 2011 standard. 


Casting

Type safety is a principal feature of the C++ language.  The casting facilities that C++ inherited from C have allowed programmers to bypass the type system and thereby weaken a compiler's ability to identify errors. 

For example, even though converting from an int to a pointer to an int is most probably meaningless, C and hence pre-standard C++ allowed:

     int x = 2;
     int* p;
     p = (int*)(x); // MOST PROBABLY AN ERROR (& missing)! 

Such syntax is nearly always an error that we should expect the type-checking system to trap.  Errors that result from such casts are very difficult to find when embedded within thousands of lines of code. 

Standard Style Casts

Good programming practice avoids casts altogether.  To strengthen the type checking system, C++98 introduced, as an alternative to C-style casts, more restrictive syntax, which is easier to locate in a large program. 

Where we cannot avoid casting, C++11 favors the syntax described in the chapter on Function Templates.  We localize casts as much as possible. 

Pre-Standard Casts

Pre-standard C++ supported casting in two forms - C-style casts or C++-function-style casts:

 (type) identifier       or      type (identfier)

These forms are interchangeable for fundamental types that are not pointer types.  For conversions to pointer types, pre-standard C++ requires C-style casts. 

C-Style Casts

To cast a value from one type to another using a C-style cast, we preface the identifier with the name of the target type enclosed in parentheses: 

 // C-Style Casting
 // c_cast.cpp

 #include <iostream>

 int main() {
     double hours;
     int minutes;
     std::cout << "Enter minutes : ";
     std::cin >> minutes;
     hours = (double) minutes / 60;  // C-Style Cast 
     std::cout << "In hours, this is " << hours;
 }

Function-Style Casts

To cast a value from one type to another using a function-style cast, we enclose in parentheses the variable or object rather than its target type:

 // Function Style Casting
 // functionStyleCast.cpp

 #include <iostream>

 int main() {
     double hours;
     int minutes;
     std::cout << "Enter minutes : ";
     std::cin >> minutes;
     hours = double(minutes) / 60;  // Function-Style Cast 
     std::cout << "In hours, this is " << hours;
 }

Differences between C++ and C

Notable differences exist between C++ and C with respect to terminology, explicit compound type identification and prototyping. 

Terminology

Definition

In C++, a definition identifies a declaration that attaches meaning to an identifier.  A definition is eventually used to allocate memory, but does not necessarily allocate it at declaration time. 

In C, a definition allocates memory for an identifer, whether a variable, an object or program code.  The term is used more conservatively. 

Derived Type

In C++, a derived type is a compound type that is derived from another compound type through inheritance.  A compound type is a structure or class that is composed of other types, which may be fundamental or compound or a combination of the two. 

In C, a derived type is a structure.  C does not support inheritance.  A structure, like a compound type in C++, is composed of other types, which may be derived types or fundamental types or a combination of both. 

Explicit Compound Type Identification

In C++, we only include the keyword that identifies a compound type (struct or class) in the definition of the type itself and in a forward declaration.  We may omit it in object and parameter definitions. 

C requires the keyword that identifies a derived type (struct) in each declaration of a derived type.  These declarations include both object definitions and parameter declarations.

For example,

 // sufficient in C++

 Student s;
 void display(const Student* s); 
 // required in C

 struct Student s; 
 void display(const struct Student* s); 

Prototyping

In C++, a function prototype must list all of its parameter types in their proper order.  This constraint enables the language to support both overloading and references. 

In C, listing the parameter types in a function prototype is optional.  The code on the right compiles successfully under a C compiler.  A C++ compiler requires the code on the left:

 // required in C++

 #include <iostream>

 void foo(int);
 int main () {
     int a = 5;
     foo(a);
 }
 void foo(int a) {
     std::cout << "a is " << a << std::endl; 
 }
 // sufficient in C

 #include <stdio.h>

 void foo();
 int main () {
     int a = 5;
     foo(a);
 }
 void foo(int a) {
     printf("a is %d\n", a); 
 }

An Empty Parameter List

In C++, an empty parameter list identicates to the compiler that the function has no parameters. 

In C, an empty parameter list has either of two interpretations:

  1. the function has no parameters
  2. the function has some parameters but the compiler should omit checking argument types against parameter types

The recommended way to specify that a function has no parameters in C is to include the keyword void between the parentheses in the function header.


Legacy Code Issues

An application written for a pre-standard C++ compiler may include code that requires upgrades to meet the stricter requirements of C++11.  Common issues include:

  • for int scope
  • original libraries
  • insufficient memory

Some compilers can still create executable files from pre-standard C++ source code.  One example is the Borland Win 32 5.5 compiler, which may be downloaded here.  Many compilers have removed pre-standard facilities and only conform to features (or a majority of features) specified in C++11.

for int scope

In pre-standard C++, the scope of a variable defined in the for clause extends beyond the closing brace of the block that encloses the iteration.  In C++11, the scope of such a variable extends only to the end of the iteration block. 

For example,

 // valid in pre-standard C++

 for (int i = 0; i < 10; i++) {
     x[i] = i * i;
     y[i] = i;
 }
 i = 0; // i is still in scope 
 // invalid in standard C++

 for (int i = 0; i < 10; i++) {
     x[i] = i * i;
     y[i] = i;
 }
 i = 0; // ERROR i is out of scope 

A C++11 compliant compiler reports this error.

Libraries

The pre-standard C++ libraries are available under the Borland Win 32 5.5 compiler, which may be downloaded here.  They are currently deprecated under the g++ GNU compiler.  They are unavailable under the Visual Studio 2012 compiler.

iostream

The pre-standard version of the iostream library is slightly more than a wrapper on the C functions prototyped in the stdio.h header file.  This pre-standard version defined the cin, cout, cerr and clog objects as global objects.  The header file for this pre-standard version was iostream.h

C++98 introduced namespaces into the core language.  The standard version of the iostream library is fully redesigned and notably larger than its predecessor.  All of the functions in the newer library are declared and defined in the std namespace.

Compare the versions shown below:

 // Pre-Standard C++
 // pre_std_io.cpp

 #include <iostream.h>

 int main() {
     cout << "Hello World!\n";
 }

 // Standard C++
 // std_io.cpp

 #include <iostream>

 int main() {
     std::cout << "Hello World!\n";
 }

C-Style Libraries

In pre-standard C++, the C libraries are prototyped in header files with .h extensions as for the C language.  Pre-standard library classes, objects and prototypes are all globally defined. 

C++98 introduced unique header file names to distinguish the standard libraries from their pre-standard predecessors.  The standard header file names begin with the character c are followed by the C-style name and do not include the .h extension.  The standard files declare the prototypes and objects in the std namespace.  This renaming convention applied wherever the standard introduced minor changes. 

For example, C++98 renamed stdio.h as cstdio.  The code for cstdio is about the same size as the code for stdio.h.  Similarly, <cstring> is the C++ version of the C header file <string.h>, <cmath> is the C++ version of <math.h>, <cstdlib> is the C++ version of <stdlib.h> and <cctype> is the C++ version of <ctype.h>.  With the standard C++ libraries, we identify the namespace:

 // sufficient in pre-standard C++

 #include <string.h>

 char name[] = "Harry";
 int len = strlen(name);
 // necessary in Standard C++

 #include <cstring>

 char name[] = "Harry";
 int len = std::strlen(name);

Insufficient Memory

The new operator allocates dynamic memory differently in pre-standard C++ than in standard C++.  If a program requests more dynamic memory than the operating system can provide the error generated a run-time differs. 

In pre-standard C++, the new operator returns the NULL address.  The following code checks for this error:

 // sufficient in pre-standard C++

 #include <iostream.h>

 int main( ) {
     char* p;
     int i = 0;

     do {
         p = new char[100001];
         i++;
     } while (p != NULL);

     cout << "Out of space after " << i << " attempts!\n"; 
 }

In Standard C++, the new operator throws an exception by default.  To instruct the operator to return NULL, we pass the nothrow argument to the operator.  nothrow is defined in the new header file:

 // required in Standard C++

 #include <new>
 #include <iostream>

 int main( ) {
     char* p;
     int i = 0;

     do {
         p = new (nothrow) char[100001];
         i++;
     } while (p != NULL);

     std::cout << "Out of space after " << i << " attempts!\n"; 
 }

In C++11, we can improve type safety by replacing NULL with the nullptr keyword: 

 // recommended in standard C++11

 #include <new>
 #include <iostream>

 int main( ) {
     char* p;
     int i = 0;

     do {
         p = new (nothrow) char[100001];
         i++;
     } while (p != nullptr);

     std::cout << "Out of space after " << i << " attempts!\n"; 
 }



Previous Reading  Previous: Function Templates Next: Table of Contents   Next Reading


  Designed by Chris Szalwinski   Copying From This Site   
Logo
Creative Commons License