Chapter 2
Basic facilities

Kurt Nørmark
Department of Computer Science, Aalborg University


Abstract
Previous lecture Next lecture
Index References Contents
In this lecture we discuss selected, basic facilities in C++, as covered in Part 1 of The C++ Programming Language (version 3).

Basic facilities
Slide Annotated slide Contents Index
References 

Basic C++ facilities beyond the facilities of ANSI C

  • Basic types. Structs in C++.

  • Declarations vs. definitions

  • Constants

  • The general object concept in C++

  • Strings in C++

  • C++ references

  • Parameter passing

  • Type conversion

  • Function overloading

  • Vectors

  • The free store

  • IO

  • Program organization - physical and logical

Fundamental types i C++
Slide Annotated slide Contents Index
References 

An overview of the fundamental types in C++

Reference

  • A boolean type

    • bool

  • Character types

    • Most important type char

  • Integer types

  • Floating point types

  • Enumeration types

  • Type void (no value)

  • Pointer types

    • Including function pointers

  • Array types

  • Reference types

  • Struct and class types

Booleans
Slide Annotated slide Contents Index
References 

There is a type bool in C++

Boolean constants true and false

Reference
  • The C++ Programming Language: Page 71

  • Type bool in relation to other fundamental types

    • Boolean values are converted implicitly to integers in arithmetic and logical expressions: false to 0 and true to 1.

    • Integers can be converted implicitly to booleans: 0 to false, other values to true.

    • Pointers can implicitly be converted to boolean values as well

Under the hood the well-known C boolean conventions still apply

Structs in C++
Slide Annotated slide Contents Index
References 

Structs and classes are almost identical concepts in C++

Reference
  • The C++ Programming Language: Page 234

  • C structs in relation to C++ structs

    • Similar to each other

    • In C++ it is possible to refer to struct S {...} by just S, not necessarily struct S

    • Both structs in C and C++ are associated with value semantics

  • C++ structs in relation to C++ classes

    • A struct is a class where all members are public by default

    • A struct is typcically used for aggregating public data

    • Both structs and classes can make use of inheritance

Declarations and definitions
Slide Annotated slide Contents Index
References 

It is important to be able to distinguish between declarations and definitions

Reference
  • The C++ Programming Language: Page 78

  • Declaration

    • Introduces names and types - for the benefit of the compiler

    • Several declarations of the same name are allowed - must agree on the types involved

  • Definition

    • A declaration that also defines an entity

      • A location in memory

      • A function with a body

    • There must be precisely one definition of a named entity

    • The one-definition-rule (ODR) relaxes this a bit - for the benefit of multiple includes:

      • A class, inline function, or template may be defined more than once

      • if they appear in different translation units

      • if they are token-for-token identical

Program: Illustration of declarations and definitions - everything is fine.
#include <iostream>
#include <string>
using std::string;

string s;                 // DEFINES s to be the empty string.
int i = 5;                // DEFINES i as an int variable initialized to 5

struct Date{              // DEFINES Date to be a type (struct) with three
  int d, m, y;            // int fields: d, m, and y
};  

int f(int i){             // DEFINES f to a particular function from
  return i + 1;           // int to int
}

extern string t;          // DECLARES that t is a string
extern int j;             // DECLARES that j an int
struct DateWithWeekday;   // DECLARES that DateWithWeekday is a struct - forward declaration
int g(int i);             // DECLARES that g is an int returning function of int

DateWithWeekday 
           h(Date, int);  // DECLARES function h from Date and int to DateWithWeekday

struct DateWithWeekday{   // DEFINES an already forwardly declared type
  Date wd;                
  int weekday;
};

int func()                // DEFINES the parameterless function func
{
  Date d1 = Date();
  DateWithWeekday 
      dwd1,
      dwd2 = h(d1, 3);

  f(i);
  g(j);
}

Program: Illustration of declarations and definitions - several problems.
#include <iostream>
#include <string>
using std::string;

string s;                 // DEFINES s to be the empty string.
string s = "AP";          // Double declaration: ERROR

int f(int i){             // DEFINES f to a particular function from
  return i + 1;           // int to int
}

int f(int j){             // DEFINES f again - double definition - ERROR
  return j - 1;           
}

double f(double j){       // DEFINES f on double - overload - OK
  return j - 1;           
}

int g(int);               // DECLARES g. OK

struct Date;              // DECLARES struct Date. OK

int f()                   // DEFINES the parameterless function f - an overload
{
  Date d;                 // Incomplete type. The details of of type Date must be known here. ERROR
  f(1);                   // OK
  g(2);                   // OK
}

Reference
  • The C++ Programming Language: ODR. Page 203

A definition is a declaration - a declaration is not necessarily a definition

The structure of a declaration
Slide Annotated slide Contents Index
References 

A declaration of a single entity consists of a four parts:

A specifier (optional), a base type, a declarator, and an initializer (optional)

Reference
  • The C++ Programming Language: Page 79 - 80

Program: Examles of declarations.
  unsigned int i = 7;
  const double d = 5.7;
  char* str[] = {"This", "is", "a", "string", "array"};
  extern short int ei;
  bool is_even(int a);
  Point p1(1,2);
  Pair q1 = {3,4};

  • Specifier:

    • Storage class: auto, extern, mutable, register, static

    • CV qualifier: const, volatile

  • Base type

  • Declarator

    • The name being declared

    • Optional declarator operators - mimics their use as operators in expressions

  • Initializer

    • Assignment-like

    • Function-like

Declaring several names together
Slide Annotated slide Contents Index
References 

Several comma-separated declarators - with associated initializers - may share the same base-type and specifier

Reference
  • The C++ Programming Language: Page 80

Program: Examles of declarations of multiple names.
  unsigned int i = 7,  j[3],  *k = &i;
  const double d = 5.7,  pi = 3.14159,  &dr = d;
  char *str[4],  ch = 'a';
  extern short int ei;
  bool is_even(int a), 
       is_odd(int a);
  Point p1(1,2), 
        p2;
  Pair q1 = {3,4}, 
       q2 = {5,6};

"Such constructs makes a program less readable and should be avoided"

Nevertheless, I use multiple name declarations frequently in these slides and notes

Declarations as statements - declarations in conditions
Slide Annotated slide Contents Index
References 

Introduce and initialize a variable at the place where it is first used

It is possible to delare variables in a condition of an if statement

Reference
  • The C++ Programming Language: Page 133, 135

  • Declarations inside blocks

    • C89: All declarations appear before the first statement

    • C99 and C++: declarations and statements can be mixed

    • Avoids having all declarations first in block, and assignments later in the block

  • Declarations in the condition of an if statement

    • The scope of such a name is the rest of the if, including the else part

    • Serves as a scope restriction, compared with a declaration just before the if statement

Program: Declarations before statements - C89 Style. Some words about compilation of these programs: I use g++ to compile C++ programs. In most examples in this material, C++ files have file extension cc. Alternatively, cpp is also a popular file extension for C++ files. If gcc is activated on a cc or cpp file, it will activate the C++ compiler. To prevent this, use the compiler option -x c. Thus, to compile this file as a C89 c program, write gcc -x c -std=c89 -pedantic less-localized.cc
double g(int a, double d){
  return a * d;
}

void f(double p){
  int i = 0;            
  double result;        // Local variable result declared before the first statement.

  //...
  if (p <= 10.0) i++;   // Some statements here              
  //...

  result = g(i,p);      // result first used much later in the function.

  /* The rest of f comes here */
}

int main(){
  f(5.0);
}    

Program: A declaration is a statement in C++.
double g(int a, double d){
  return a * d;
}

void f(double p){
  int i = 0;

  // ...
  if (p <= 10.0) i++;        // Some statements
  // ...

  double result = g(i,p);    // Declaration of result the first time its is used.
                             
  /* Rest of f comes here  */
}

int main(){
  f(5.0);
}    

Program: A declaration in the condition of an if.
double g(int a, double d){
  return a * d;
}

double f(double p){
  int i = 0;

  if (p <= 10.0) i++;             // Some statements 

  if (double result = g(i,p)){    // A declaration in a condition
    return result * result;
  }
  else{
    return 1 / result;
  }

  /* Rest of f comes here */
}

int main(){
  f(5.0);
}    

Constants
Slide Annotated slide Contents Index
References 

The const qualifier can be used in several different contexts in C++

Constness is a fairly complicated matter in C++

Reference
  • The C++ Programming Language: Page 94 - 97

  • The basics

    • The const qualifier can be applied to objects, types, and member functions

    • Constant objects cannot be mutated

  • Pointers and const

    • Either a constant pointer or a pointer to something constant

      • this is a constant pointer

    • *const is declarator operator - constant pointer

      • Other declarator operators: *, [], and ()

  • References and const

    • By nature a reference is constant - once the reference is establised it cannot be changed

  • Member functions and const

    • A constant member function cannot modify the state of the object

Program: Examles constants, pointers to constants, and constant pointers.
#include <iostream>
#include <string>
#include "point.h"

int main(){
  Point p(1,2);                 // p is a Point 
  p.move(0,1);

  const Point q(3,4);           // q is a constant point.
  q.move(1,2);                  // error (compile time)

  const Point *r = &p;          // r is pointer to constant point
  r->move(1,2);                 // error (compile time)

  Point *const s = &p;          // s is a constant pointer to 'the point p'. *const is a 'declarator operator'.
  s->move(1,2);                 // ok
  s = NULL;                     // error: Assignment of read-only variable s. (Compile time)

  Point const* w = &p;          // w is a pointer to a constant point.  const* is NOT a 'declarator operator'.
                                // Point const ...  and  const Point means the same.
  const Point *w = &p;          // Equivalent to the line above. But here w is illegally defined twice, of course.
  w->move(1,2);                 // error (compile time). Cannot mutate a constant point.

  const Point *const t= &p;     // t is a constant pointer to a constant point.
  t->move(1,2);                 // error (compile time)
  t = NULL;                     // error (compile time)

  Point const *const v= &p;     // Same as definition of t. 
}

The general notation of objects in C++
Slide Annotated slide Contents Index
References 

Objects in C++ have a generalized meaning compared to many other object-oriented programming languages

The concept object: An object is "something in memory"

Reference
  • The C++ Programming Language: Page 84

  • The low-level notation of a C++ object

    • Something in memory - a contiguous region of storage

    • An array

    • A variable

An instance of a class is both an object in the low-level meaning, and the well-established OOP meaning of the word

Lvalues
Slide Annotated slide Contents Index
References 

The concept object: An object is "something in memory"
The concept lvalue: An lvalue is an expression that refers to an objectThus, an lvalue is an expression that refers to a memory location. You can take the address of that memory location with the address operator.

Reference
  • The C++ Programming Language: Page 84

  • Lvalues

    • Named after expressions that can occur at the left-hand side of an assignment

    • As such, lvalues are typically modifiable

    • Constant lvalues do also occur in C++ (in the context of so-called const references, §5.5)

Program: Examples of lvalues.
  var = 7;
  tab1[3] = 8;
  *(tab2 + 4) = 9;
  tab3[0].x = 2.0;

C-style strings
Slide Annotated slide Contents Index
References 

C++ supports both C-style strings and type String via the Library.

Reference
  • The C++ Programming Language: Page 90

  • C-style strings: char* or char[]

    • A pointer to the first character in an array of characters

    • Zero terminated

    • A string literal in C++ program, such as "string literal", is not of type String

    • Almost identical to string handling in C programs

Both C-style strings and objects or type string are mutable.

C++ style strings
Slide Annotated slide Contents Index
References 

It is recommended to use type string instead of the low-level char* or char[]

Reference
  • The C++ Programming Language: Page 48-49, 579

  • Type string in C++:

    • string is alias for a basic_string parameterized by char:

      • typedef basic_string<char> string

    • Strings obey value semantics

      • Copied in and out of functions

    • Characters in strings can be accessed in checked mode and unchecked mode:

      • Checked mode: s.at(i)

      • Unchecked mode: s[i]

    • A string is mutable

    • Strings can be copy constructed based on a C-style string

      • string s("Peter");     or     string t = "Peter";

    • The basic_string class supports a large number of string operations

Strings - examples
Slide Annotated slide Contents Index
References 

We show simple examples that illustrate the value semantics of C++ strings

Reference
  • The C++ Programming Language: Page 588

Program: The Knold & Tot example from section 20.3.6 of The C++ Programming Language.
// From Stoustrup, The C++ Programming Language, 3ed ed, page 588.
// Illustrates value semantics of C++ strings.

#include <iostream>
#include <string>

void g(){
  std::string s1 = "Knold",
              s2 = "Tot";

  s1 = s2;            // Now s2 is "Tot" and s1 is another copy of "Tot".
  s2[1] = 'u';        // s2 is mutated. Now "Tut".

  std::cout << s1 << " og " << s2 << std::endl;             // Tot og Tut
}

int main(){
  g();
}

Program: The Knold & Tot example with char*.
// Illustrates reference semantics of C-like strings.

#include <iostream>

void g(){
  char *s1 = "Knold",  // Compiler Warnings:
       *s2 = "Tot";    // deprecated conversion from string constant to char*

  s1 = s2;             // Both s1 and s2 points to the same "Tot" C-style string.
  s2[1] = 'u';         // Expected logical error here...
  
  std::cout << s1 << " og " << s2 << std::endl;  // Tut og Tut
}

int main(){
  g();
}

Program: The Knold & Tot example with char[].
// Illustrates that C-like strings cannot be assigned.
// The similar assignment is possible on C++ strings.

#include <iostream>

void g(){
  char s1[6] = "Knold",
       s2[6] = "Tot  ";

  s1 = s2;           // Compiler error: Invalid array assignment
  s2[1] = 'u';       // OK.
  
  std::cout << s1 << " og " << s2 << std::endl; 
}

int main(){
  g();
}

Program: The Knold & Tot example with strcpy from <cstring>.
// Use of strcpy - remedies the problem in the previous C program.

#include <iostream>
#include <cstring>    // Corresponds to <string.h> in a C program.

void g(){
  char s1[] = "Knold",
       s2[] = "Tot";

  std::strcpy(s1, s2);   // Works because "Tot" is not longer than "Knold".
  s2[1] = 'u';
  
  std::cout << s1 << " og " << s2 << std::endl;   // Tot og Tut

}

int main(){
  g();
}

Reference

Program: Illustration of various basic string operations in C++.
// Illustrates use of of various C++ string functions.

#include <iostream>
#include <string>
#include <cstdlib>

using std::string;
using std::cout; using std::endl;

int main(){
  // CONSTRUCTION:
  string s0,         // the empty string
         s1 = "A string",
         s2("Another string"), 
         s3(s1),     // copy constructor
         s4(s1,2,6); // copy constructor, selected part
                     // 6 characters from s: "string"

  cout << s4 << endl; // "string"
  
  // ELEMENT ACCESS:
  cout << "s1[0] = " << s1[0] << endl;        // A
  cout << "s1[10] = " << s1[10] << endl;      // Index out of range. Risky at run time!

  cout << "s1[0] = " << s1.at(0) << endl;     // A
//cout << "s1[10] = " << s1.at(10) << endl;   // Compiles. Run time error : out of range.

  // ASSIGNMENT:
  s0 = s2;                                    // Assignment - copies all chars in s2.
  cout << s0 << endl;                         // "another string"

  // CONVERSION TO C-STYLE STRINGS:
  s1 = "1234";
  const char *str1 = s1.c_str();              // C++ string   ->  C string
  int str1_int = std::atoi(str1);             // C-style function: atoi applied
//int s1_int = std::atoi(s1);                 // Not possible to call atoi on a C++ string:
                                              // Cannot convert string to const char* 
  cout << "char*: " << str1                   // char*: 1234
       << " int: "  << str1_int << endl;      // int: 1234

  // COMPARISON:
  cout << "compare abc and def: "
       << string("abc").compare("def")        // -1
       << endl; 
 
  s1 = s2;
  cout << "compare s1 and s2: "               // s2 has just been copied to s1 - thus equal.
       << s1.compare(s2) << endl;             // 0

  // INSERT AND APPEND:
  string s5 = "Poul", s6 = "Hansen";
  s5 += ' ';
  s5 += s6;
  cout << "Appended names: " << s5 << endl;   // Poul Hansen

  s5.insert(5, "Ib ");                        // Insert "Ib" as middle name.
  cout << "Insert Ib middle name: " << s5     // Poul Ib Hansen
       << endl;

  s5 = "Peter", s6 = "Andersen";
  cout << s5 + " " + s6 << endl;              // Concatenation: 
                                              // Peter Andersen
}

Program: Program output.
string
s1[0] = A
s1[10] = 
s1[0] = A
Another string
char*: 1234 int: 1234
compare abc and def: -1
compare s1 and s2: 0
Appended names: Poul Hansen
Insert Ib middle name: Poul Ib Hansen
Peter Andersen

Exercise 2.2. String functionsContinue the programming from above with a C++ program that uses the find, replace, substr, and size functions from The C++ Programming Language section 20.3.11 - 20.3.14.

References
Slide Annotated slide Contents Index
References 

A reference in C++ is an alias

A reference is an alternative name for an object

Reference
  • The C++ Programming Language: Page 97

Program: C++ References.
  int k = 3;
  double a = 5.0;
  double &d = a;
  void f(int &i, double &d);
  f(k,a);

Reference

  • Why references in C++?

    • An implementation of call-by-reference parameters - instead of relying on pointers passed by value

    • An optimization of large call-by-value parameters - by means of const references (meaning: a reference to a constant)

      • Name binding to temporary objects

    • Essential for parameters of copy constructors and assignment operator overloads

Reference
  • The C++ Programming Language: Argument passing. Value return. Page 145-148

A reference in C++ can be understood as a generalization of "call by reference parameters"

Rules for references
Slide Annotated slide Contents Index
References 

Program: C++ References.
  int k = 3;
  double a = 5.0;
  double &d = a;
  void f(int &i, double &d);
  f(k,a);

  • A reference must be initialized when declared

  • A reference is constant by nature

    • Once established the reference can never be set to reference something else

  • No operator operates on a reference as such - only the object referenced

  • Pointers to references and arrays of references do not exist

    • But references to pointers and references to array can exists

"The absence of const in a declaration of a reference argument is taken as a statement of intent to modify the variable"

Reference
  • The C++ Programming Language: Page 146

References - Examples
Slide Annotated slide Contents Index
References 

Here we show a number of examples that illustrate various aspects of references

Program: A variable becomes a reference to another variable.
// Very simple example - illustrates an alias e of d in f.

#include <iostream>
#include <string>
using namespace std;

double f(double di = 6.0){
  double d = di,  // d initialized to the value of the parameter di
         &e = d;  // e is an alias to d

  e += 7.0;       // increasing e, and hereby d, by 7
  return d;
}

int main(){
  cout << f() << endl;   // 13
  cout << f(8) << endl;  // 15
}

Program: No operator operates on a reference as such.
// Example similar to the function g on page 98 in 'The C++ Programming Language'.

#include <iostream>
#include <string>

using namespace std;

void g(){
  int ii = 0;
  int& rr = ii;              // rr is a reference to ii - an alias to ii
  rr++;                      // ii is incremented.
                             // The reference is NOT incremented itself.
  cout << ii << endl;        // 1
  cout << rr << endl;        // 1

  // NOW WE DO SIMILAR THINGS ON POINTERS:
  int* pp = &rr;             // pp is really the address of ii (via rr) - a pointer to ii.
                             // NOT a pointer to a reference!
  (*pp)++;                   // ii is incremented again, 
  pp++;                      // The pointer as such is incremented - pointer arithmetic.
                             // Not good...
  cout << ii << endl;        // 2
  cout << (*pp) << endl;     // 2673944
  cout << (*(pp-1)) << endl; // Still 2
}

int main(){
  g();
}

Program: A function with reference parameters and reference return type.
// Illustrates call by C++ reference parameters. 

#include <iostream>
#include <string>

int& f(bool b, int& i, int& j){
  if (b)
    return i;
  else 
    return j;
}

int main()
{
  using namespace std;

  int a, b;
  a = b = 0;

  f(true, a, b) = 7;            // assigning to a
  cout << "a: " << a << endl;   // 7
  cout << "b: " << b << endl;   // 0

  a = b = 0;
  f(false, a, b) = 7;           // assigning to b
  cout << "a: " << a << endl;   // 0
  cout << "b: " << b << endl;   // 7
}

Program: Convenient references to long/deep fields.
// A program with two versions of the function work_on_persons_road_number.
// Illustrates how a reference can be used for naming convenience.

#include <iostream>
#include <string>

using namespace std;

typedef struct{
  char road[15];
  int roadNumber;
  char town[20];
  } address;   

typedef struct{
  int idNumber;
  char firstName[10],
       lastName[20];
  address location;} person;   

bool even(int n){
  return n%2 == 0;
}

// A function in which a long and deep field name is used many times.
void work_on_persons_road_number(person &p){
  if (even(p.location.roadNumber))
    p.location.roadNumber = p.location.roadNumber + 1;
  else
    p.location.roadNumber = 0;
}

// A version of the function that binds the roadNumber field to an
// int reference.
void work_on_persons_road_number_with_ref(person &p){
  int &lrn = p.location.roadNumber;
  if (even(lrn))
    lrn = lrn + 1;
  else
    lrn = 0;
}

void print_person(person &p){
  cout << p.firstName << " " << p.lastName << endl
       << p.location.road << " " << p.location.roadNumber << endl
       << p.location.town << endl << endl;
}
 
int main(){
  person morten = 
     {190583,                             /* Initializer      */
      "Morten", "Madsen",                 /* and only that... */
      {"Bredgade", 23, "Middelfart"}
     };

  print_person(morten);
  work_on_persons_road_number(morten);
  print_person(morten);
  work_on_persons_road_number_with_ref(morten);
  print_person(morten);
}

Exercise 2.4. Another example/exercise with C++ references

On the accompanying slide we have shown a number of examples that illustrate different uses of reference types in C++.

Your task in this exercise is to come up with yet another good and convincing example of reference types in C++. (Please use your own fantasy - not some internet fantasy).

Your program can be seen as the solution to a programming exercise about references. Please provide a short formulation of this exercise.

Exercise 2.4. Pointers in combination with references

In this exercise we will explore pointers and references relative to each other. Play and learn! We do this in context of a function that returns the maximum of two doubles. Here is the function with call-by-value parameters:

  double max(double a, double b){
    return a < b ? b : a;
  }

In all cases below, program the max function, call it, make make sure you get the expected result from the call.

  1. As a warm up: Make a version with call by const references instead of call by value parameters.
  2. Demonstrate that pointers to references do not exist.
  3. Make a version with references to pointers. Does that make sense to you?
  4. Next, make version with const references to pointers.
  5. Finally, a version with const references to pointers to double constants

Can you imagine other interesting variations?

The The C++ Programming Language example on page 99-100 is also illustrative

Constant References
Slide Annotated slide Contents Index
References 

A constant reference is really a reference to a constant

"An initializer for const T&   does not need to be an lvalue, or even of type T."

Reference
  • The C++ Programming Language: Page 98, 146, 148, 283

Program: Const ref scheme.
  const T &var = expression;

  • Initialization of var - a reference to T

    • Implicit conversion from the type of expression to T is performed

    • The result is placed in a temporary variable of type T

    • var is bound to the tempory variable - which cannot be mutated

It would be undesirable and misleading to be able to modify var, because it is just a temporary variable.

Program: The initialization of var in a real, but simple context.
// Initializes a const reference with a rvalue.

#include <iostream>
#include <string>
#include <cmath>

typedef int T;

int main(){
  using namespace std;

  double expression = 5.3;

  const T &var = expression;  // var becomes a reference to a rvalue 
                              // via a temporary variable.

  cout << var << endl;        // 5
}

Program: A similar program that initializes a user defined struct via a 'functional casting' constructor.
// Shows a type conversion before the binding of the const reference.

#include <iostream>
#include <string>
#include <cmath>

struct TwoDoubles{
  double f1, f2;

  TwoDoubles(double d){          // A TwoDobles object can be constructed from a double
    f1 = d/2; f2 = d/2;
  }
};


int main(){
  using namespace std;

  const TwoDoubles &var = 6.4;   // A temporary TwoDoubles object is made, by
                                 // activating the Twodoubles constructor on 6.4.
                                 // var becomes a const reference to the
                                 // TwoDoubles object.

  cout << var.f1 << ", " << var.f2 << endl;  // 3.2,  3.2
}

Program: An envelope around the stdlib div function that returns a struct of quotient and remainder.
// C++ In A Nutshell example, page 36. Three version of divide.
// div returns an object that aggregates the quotient and the remainder.

#include <iostream>
#include <cstdlib>

// The type ldiv_t is a struct with a quotient and a remainder field.

void divide_1 (long num, long den, long& quo, long& rem){
  std::ldiv_t result = std::div(num, den);          // The struct is COPIED out of div
  quo = result.quot;                    
  rem = result.rem;
}

void divide_2 (long num, long den, long& quo, long& rem){
  const std::ldiv_t& result = std::div(num, den);  // The struct is NOT COPIED out of div.
  quo = result.quot;                               // Instead a const reference is established 
  rem = result.rem;                                // to the resulting struct.
                                                   // This most likely involves creating a temporary struct, however.
}

void divide_3 (long num, long den, long& quo, long& rem){
  std::ldiv_t& result = std::div(num, den);        // error: invalid initialization of non-const reference of type ldiv_t&
  quo = result.quot;                                       
  rem = result.rem;                       
} 

int main(){
  using namespace std;

  long  q, r;

  divide_1(107, 20, q, r);
  cout << q << " remainder " << r << endl;  // 5 remainder 7

  divide_2(107, 20, q, r);
  cout << q << " remainder " << r << endl;  // 5 remainder 7
}

Exercise 2.5. Ways of returning results from the div function

This exercise works in the context of the divide program from the slide about constant references.

Program the struct ldiv_t and the function div yourself, instead of using the definitions from the std namespace.

Run the program with your own versions of ldiv_t and div.

Experiment with returning a reference ldiv_t& from div. (See page 282-283 in The C++ Programming Language).

And similarly, experiment with returning a const reference const ldiv_t& from the div.

Program: A similar setup - illustrates that it is not good to return a reference to a deallocated local variable.
#include <iostream>
#include <string>
#include <cmath>

using namespace std;

typedef int T;

const T& f(double d){
  double e = 2 * d;
  return e;                      // Compiler warning:
                                 // returning reference to temporary
}

int main(){
  double expression = 5.3;

  const T &var = f(expression);  
                                 
  cout << var << endl;           // ??
}

References versus Pointers
Slide Annotated slide Contents Index
References 

Examples that compare programming with pointers and C++ references

Program: Two versions of swap - with references and with pointers.
#include <iostream>
using namespace std;

void swap_doubles_by_ref(double &d1, double &d2){
  double temp;
  temp = d1;
  d1 = d2;
  d2 = temp;
}

void swap_doubles_by_ptr(double *d1, double *d2){
  double temp;
  temp = *d1;
  *d1 = *d2;
  *d2 = temp;
}

int main(){
  double e, f;

  e = 5.0, f = 7.0;
  cout << e << " " << f << endl;   // 5 7
  swap_doubles_by_ref(e, f);  
  cout << e << " " << f << endl;   // 7 5

  e = 5.0, f = 7.0;
  cout << e << " " << f << endl;   // 5 7
  swap_doubles_by_ptr(&e, &f);  
  cout << e << " " << f << endl;   // 7 5
}

  • Observations

    • The notational overhead - use of & and * - is less for use of references than for pointers

      • The reason is that it is not possible to manipulate the reference as such

    • The mental models are different

      • Pointers: Establish pointer to an object, follow the pointer to the object for lhs/rhs access

      • References: Establish an alternative name to an existing object

A reference may be implemented as a constant pointer that is dereferenced automatically each time it is used

Parameter passing in C++
Slide Annotated slide Contents Index
References 

It is possible to pass parameters by value, by a pointer, and via a C++ reference

  • Call by value

    • Copy actual parameters and bind to formal parameters

      • Formal parameters are initialized - not assigned.

      • For user-defined types: It is possible to control what copying means - via a copy constructor

      • In the copy constructor itself, call-by-C++-reference parameter passing is crucial.

    • In the special case: Passing pointers by value (C-style call-by-reference)

  • Call by (C++) reference

    • The formal parameter becomes an alias of the actual parameter

    • Call by const reference is an attractive alternative to call by value parameters

      • No overhead due to copying

      • No risk of modifying the actual parameter via the formal parameter

Value return
Slide Annotated slide Contents Index
References 

The semantics of value return is that of initialization

Reference
  • The C++ Programming Language: Page 148

  • Value return

    • A copy is returned, initializing an unnamed variable

    • The copy constructor is used - not the assignment operator

  • Return of local variables

    • OK by value

    • References or pointers to local (automatic) variables should not be returned

Type conversion - Grand Overview
Slide Annotated slide Contents Index
References 

The purpose of this page is to give an overview of implicit and explicit type conversion in C++

Reference
  • The C++ Programming Language: Page 130-131, 408, 819

  • Implicit conversions

    • Between built-in, fundamental (numeric) types - some details next page

    • Between user-defined types and built-in types

      • Via constructors and conversion operators

      • More details in the next lecture

    • In between user defined types

  • Explicit conversions to type T:

    • C-style type casts: (T)expr   or   T(expr) using early C++ notation

    • static_cast<T>(expr) from a related type

    • reinterpret_cast<T>(expr) from an unrelated type   -   "dangerous"

    • dynamic_cast<T*>(expr) from a related polymorphic type (pointer or reference types only)

    • const_cast<T>(expr) from a closely related type (add or remove const qualifier)

Item 2 of More Effective C++ has some basic arguments in favor of 'the new C++ casts' in contrast to C-style casts.

Implicit type conversions
Slide Annotated slide Contents Index
References 

"Integral og floating-point types can be mixed freely in assignments and expressions"

Reference
  • The C++ Programming Language: Page 833

  • "Fundamental types can be converted to each other in a bewildering number of ways"

  • Promotions

    • A kind of normalization to certain integer types and floating point types before arithmetic operations

  • Usual Arithmetic conversions

    • Convert arguments of binary operators to a common type

  • Conversion to boolean

    • From pointers, integers, and floating point types

    • Non-zero value transformed to true and zero to false

  • Conversion between integer types

    • Unsigned: Cutting off bits

    • Signed: Implementation dependent

  • Conversion between floating point and integer types

    • May cause loss of information, or may be undefined (in case of range problems)

Explicit type conversion
Slide Annotated slide Contents Index
References 

C++ supports a number of different kinds of type castings

Use of these cast operations makes it easier for the programmer to spot the more dangerous type conversions

Reference
  • The C++ Programming Language: Page 130-131, 407-408 (dynamic casts), 819

  • static_cast<T>(expr)

    • Casting between related types

    • Can activate built-in or user-defined type conversion, conversion operators, and implicit/explicit constructors.

    • Cannot cast away const

  • reinterpret_cast<T>(expr)

    • Casting between unrelated types, potentially unsafe

    • Can cast between pointer types, and between integer and pointer types.

    • ... provides a value of new type that has the same bit pattern as its argument

  • dynamic_cast<T>(expr)

    • Primarily for downcasting or crosscasting of a pointer to an object (of polymorphic type)

    • For a pointer p, dynamic_cast<T>(p) can be seen as the question: 'Is the object pointed to by p of type T ?'.

  • const_cast<T>(expr)

    • Add or remove const (or volatile) qualifier to a type

Probably the most frequently used, see item 2 of More Effective C++

Function Overloading
Slide Annotated slide Contents Index
References 

Functions can be overloaded in C++

Reference
  • The C++ Programming Language: Page 149-153

  • Function overloading

    • Several function definitions with the same name, but different formal parameters

    • For a given function call, f(a1, ..., an), the types of the actual parameters are used to select a single best match among the overloaded function definitions

      • If no single best match can be found, an ambiguity is reported

      • Overload resolution is a compile time issue

      • Overload resolution does not affect the run time characteristic of the program

    • The function return types do not take part in overload resolution

  • Finding the best match

    • Pairs of actual and formal parameters are compared - details on the next page

    • A function that is the best match for one argument and a better than or equal match for all other arguments is called (§7.4.4)

      • The call is ambiguous if no such function exists

Function Overloading - more detailed rules
Slide Annotated slide Contents Index
References 

Matching the type of an actual parameter and the type of a formal parameter

The criateria below are tried in order

The call is ambiguous if two or matches are found at the same level

Reference
  • The C++ Programming Language: Page 149

  • Exact match

    • Only trivial conversions are performed

    • Array name to pointer, function name to function pointer, T to const T,...

  • Match using promotions

    • Integral promotions and floating point promotions

    • bool to int, char to int, short to int, float to double, ...

  • Match using standard conversions

    • int to double, double to int, double to long double derived* to base*, T* to void, ...

  • Match using user defined conversions

    • Conversion operators and constructors

  • Match using the ellipsis ...

    • Unspecified number of arguments

Function Overloading - Examples
Slide Annotated slide Contents Index
References 

We show examples of single best match and ambiguous function overloading

Program: Exact matches - a trivial example.
#include <iostream>
#include <string>

using namespace std;

void f(long int i){
  cout << "f(long int)" << endl;
}

void f(short int i){
  cout << "f(short int)" << endl;
}


int main(){
  long int a = 5;
  f(a);             // exact match: f(long ing)

  short int b = 5;
  f(b);             // exact match: f(short int)
}

Program: Simple examle of an ambiguity.
#include <iostream>
#include <string>

using namespace std;

void f(long int i){
  cout << "f(long int)" << endl;
}

void f(short int i){
  cout << "f(short int)" << endl;
}


int main(){
  int c = 5;
  f(c);             // error: call of overloaded f(int&) is ambiguous
                    // note: candidates are: void f(long int)
                    // note:                 void f(short int)

}

Program: An ambiguity between 'float to int' and 'float to long int'.
#include <iostream>
#include <string>

using namespace std;

void f(int i){
  cout << "f(int)" << endl;
}

void f(long int i){
  cout << "f(long int)" << endl;
}





int main(){
  float c = 5.5;
  f(c);             // error: call of overloaded  f(float&)  is ambiguous
                    // note: candidates are: void f(int)
                    // note:                 void f(long int)

}

// This example is continued in the next program...

Program: 'Float to double' conversion prefered over 'float to int' and 'float to long int'.
#include <iostream>
#include <string>

using namespace std;

void f(int i){
  cout << "f(int)" << endl;
}

void f(long int i){
  cout << "f(long int)" << endl;
}

void f(double d){
  cout << "f(double)" << endl;
}

int main(){
  float c = 5.5;
  f(c);             // A single best match:  f(double)  
                    // The float to double promotion is in another category than
                    // float -> int  and  float -> long int.
}

Program: Point.h.
// Now overloading examples with class Point.
// Class point with a constructor of type double, and a conversion from Point to double.

class Point {
private: 
  double x, y;

public:
  Point(double d);              
  operator double() const;      

  double getx () const;
  double gety () const;
};

std::ostream& operator<<(std::ostream&, const Point&);

Program: double to Point via Point(double) constructor.
#include <iostream>
#include <string>
#include "point.h"

using namespace std;

void f(Point p){
  cout << "f(Point)" << endl;
}

void f(char *c){
  cout << "f(char *)" << endl;
}









int main(){
  double c = 5.5;        
  f(c);                // A single best match: f(Point)
                       // f(char *) does not match at all.
}

// This example is continued in the next program...

Program: 'double to char' instead of 'double to Point'.
#include <iostream>
#include <string>
#include "point.h"

using namespace std;

void f(Point p){
  cout << "f(Point)" << endl;
}

void f(char *c){
  cout << "f(char *)" << endl;
}

void f(char c){
  cout << "f(char)" << endl;
}





int main(){
  double c = 5.5;        
  f(c);                   // A single best match:  f(char)
                          // The conversion double -> char is in a higher category than
                          // the user-defined double -> Point
} 

// This example is continued in the next program...

Program: Now in an ambiguity situation.
#include <iostream>
#include <string>
#include "point.h"

using namespace std;

void f(Point p){
  cout << "f(Point)" << endl;
}

void f(char *c){
  cout << "f(char *)" << endl;
}

void f(char c){
  cout << "f(char)" << endl;
}

void f(float c){
  cout << "f(float)" << endl;
}

int main(){
  double c = 5.5;      
  f(c);                // error: call of overloaded  f(double&)  is ambiguous
                       // note: candidates are: void f(Point)
                       // note:                 void f(char)
                       // note:                 void f(float)
}

Program: A single best match again - slightly surprising perhaps.
#include <iostream>
#include <string>
#include "point.h"

using namespace std;

void f(char *c){
  cout << "f(char *)" << endl;
}

void f(float c){
  cout << "f(float)" << endl;
}

void f(string c){
  cout << "f(string)" << endl;
}

void f(...){
  cout << "f(...)" << endl;
}

int main(){
  Point p(5.5);   
  f(p);           // A single best match: f(float)
                  // the conversion operator point -> double,
                  // and a double to float conversion  

                  // f(char *) and f(string) do not match at all.
                  // f(...) matches, but it is in a lower matching category
}

Program: A trivial example with overloading of a function of two parameters.
#include <iostream>
#include <string>

using namespace std;

void f(int i, double d){
  cout << "f(int, double)" << endl;
}

void f(double d, int i){
  cout << "f(double, int)" << endl;
}

int main(){
  f(5, 5.5);     // f(int, double)
  f(5.5, 5);     // f(double, int)
  f(5, 6);       // Ambiguous
  f(5.6, 6.5);   // Ambiguous
}

Vectors in C++
Slide Annotated slide Contents Index
References 

C++ has a flexible vector type, as an alternative to the C-like array facility

Reference
  • The C++ Programming Language: Page 52, 442

References

  • Vector characteristics:

    • A template class - type parameterized

    • Contiguously allocated - like a native array.

    • Not of fixed size like an array - the number of elements may be adjusted dynamically

    • Size and capacity:

      • Size: The actual number of elements in the vector

      • Capacity: The maximum number of elements before resizing is needed

    • With or without range checking: The programmer's choice

    • Obeys value semantics

      • If too expensive, pointers or references to vectors can be used

    • Traversed with use of iterators

      • Iterators can be used in a similar way as pointers, via overloading of operators

      • *iterator, iterator++, iterator+n, iterator1-iterator2

Exercise 2.6. Understand Vectors

Take a look at the documentation of the (template) class vector at www.cplusplus.com

Familiarize yourself with the vector member functions, and read some of the (numerous) small examples which are available

Vectors - examples
Slide Annotated slide Contents Index
References 

We provide a couple of program examples that use the template class vector

The first is similar to the initial array example

Reference

Program: A simple vector example - similar to first array program.
// Similar to array-1.c discussed earlier.
#include <iostream>
#include <string>
#include <vector>

// Using declarations:
using std::string;
using std::vector;
using std::cout; using std::endl;

int main(){
  // Vector construction:
  vector<double> a;  // An empty vector of element type double
  double sum;

  // Adding elements to the back end:
  for (vector<double>::size_type i = 1; i <= 5; i++)
    a.push_back(i);

  // Mutation of an EXISTNG ELEMENT:
  a[1] = 2.2;

  // Sum up the elements - in a simple for-loop:
  sum = 0.0;
  for (int i = 0; i < a.size(); i++){
    sum += a[i];
  }
  cout << "Sum = " << sum << endl;  // 15.2

  // Sump up the elements - with iterators:
  sum = 0.0;
  for (vector<double>::iterator iter = a.begin();
       iter != a.end();
       iter++){
    sum += *iter;
  }
  cout << "Sum = " << sum << endl;  // 15.2
}

Program: Another vector example - constructors, insertion, lexicographic comparison.
#include <iostream>
#include <string>
#include <vector>

using std::string;
using std::vector;
using std::cout; using std::endl;

void print_vector(string name, vector<string> &sv){
   cout << name << ":" << endl;
   for (vector<string>::iterator iter = sv.begin();
       iter != sv.end();
       iter++)
     cout << "  " << (*iter) << endl;
   cout << endl;
}    

int main(){
  // Vector constructing:
  vector<string>
     sv1,                // An empty vector of strings
     sv2(10),            // A vector of 10 empty strings
     sv3(5, "AP"),       // A vector of 5 strings each "AP"
     sv4(4, "KN"),       // A vector of 4 strings each "KN"
     sv5(sv3.begin(), sv3.begin() + 3);
                         // A vector copied from front of sv3

  // Change every second element of sv3 to "PA":
  for (vector<string>::iterator iter = sv3.begin();
       iter < sv3.end();
       iter += 2){
    (*iter) = "PA";
  }  

  print_vector("sv3", sv3);

  // Insert 3 elements from sv4 near the end of sv3:
  sv3.insert(sv3.end()-1, sv4.begin(), sv4.begin()+3);

  print_vector("sv3", sv3);

  print_vector("sv4", sv4);   print_vector("sv5", sv5);

  // Lexicograhpic comparison between sw4 and sw5:
  if (sv4 == sv5)
    cout << "sv4 is equal to sv5" << endl;
  else if (sv4 < sv5)
    cout << "sv4 is less than sv5" << endl;
  else 
    cout << "sv4 is greater than sv5" << endl;
}

Program: Program output.
sv3:
  PA
  AP
  PA
  AP
  PA

sv3:
  PA
  AP
  PA
  AP
  KN
  KN
  KN
  PA

sv4:
  KN
  KN
  KN
  KN

sv5:
  AP
  AP
  AP

sv4 is greater than sv5

The free store
Slide Annotated slide Contents Index
References 

Reference
  • The C++ Programming Language: Page 127

The C++ operators new and delete are used instead of the <cstdlib> functions malloc / calloc and free

  • new

    • Application: new T, where T is a type

    • Returns a pointer to the new object

    • The object allocated by new lives until it is deallocated with delete

    • new returns a pointer to uninizialized memory

  • delete

    • Application: delete pT where pT is a pointer returned by new

    • A variant delete[] pT must be used if pT is a pointer to an array allocated by new

Program: Illustration of free store.
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

using namespace std;

int main(){

  // An int:
  int *pi = new int;             // Allocate an int
  *pi = 5;
  cout << 6 * *pi << endl;       // 30
  delete pi;                     // Deallocate the int


  // An array of int:
  int *ia = new int[10];         // Allocate an array of 10 ints
  for(int i = 0; i < 10; i++)    // Initialize array
     ia[i] = 2 * i;

  for(int i = 0; i < 10; i++)    
    cout  << setw(3) << ia[i];   // 0  2  4  6  8 10 12 14 16 18
  cout << endl;

  delete [] ia;                  // Deallocate the array


  // A vector of ints:
  vector<int> *vd = 
      new vector<int>(10);       // Allocate a vector

  for(int i = 0; i < 5; i++)     // Mutate half of the vecctor  
    (*vd)[i] = 2 * i;

  for(int i = 0; i < 10; i++)    
    cout << setw(3) << (*vd)[i]; // 0  2  4  6  8  0  0  0  0  0

  cout << endl;

  delete vd;                     // Deallocate the vector

}

"A C++ implementation does not guarantee the prescence of a 'garbage collector'" [Stoustrup page 128]

Input and output in C++
Slide Annotated slide Contents Index
References 

File IO - including standard input and standard output - is supported via the libraries ostream and istream

Overloading of the operators >> ('get from') and << ('put to') is used for textual input and output respectively

Reference
  • The C++ Programming Language: Page 607, 613

  • Considerations about IO in C++

    • IO functions must apply uniformly to pre-defined types and user-defined types

      • The C-style printf and scanf are not easy to generalize to user-defined functions

      • In addition, printf and scanf are not type safe

    • Use of overloaded functions, such as put(data) and get(data)

      • repetitive and tedious in real-life situtions

      • s.put(d1); s.put(d2); s.put(d3);     or

      • s.put(d1).put(d2).put(d3);

    • Use of overloaded operators seems attractive

      • Shorter, and easier to spot

      • s << d1 << d2 << d3

Overloaded operators and IO
Slide Annotated slide Contents Index
References 

Existing operators must be choosen for IO purposes

  • The assignment operator was considered

    • cin = x   and   cout = y

    • Its association to the right is inappropriate

  • The less than and greater than operators were considered

    • cin > x   and   cout < y

    • Unreadable, and confusing

  • The left shift and right shift operators were choosen

    • cin >> x   and   cout << y

    • The relative low priority of << is practical when values of expressions must be printed.

    • cout << x + y * z   and not   cout << (x + y * z)

Reference
  • The C++ Programming Language: Page 121-122: operator priorities

Standard streams
Slide Annotated slide Contents Index
References 

Like most other programming languages C++ supports a number of standard input and output streams

Reference
  • The C++ Programming Language: Page 609, 614

  • cin and wcin

    • Standard input of char and wchar_t respectively

  • cout and wcout

    • Standard output

  • cerr and wcerr

    • Standard error - unbuffered

  • clog and wlog

    • Standard output stream for error messages

Stream State
Slide Annotated slide Contents Index
References 

Reference
  • The C++ Programming Language: Page 616

Streams in C++ have state associated that represents error conditions and formatting details

  • Error condition or non-standard condition - class basis_ios

    • strm.good()

    • strm.eof()

    • strm.fail()

    • strm.bad()

  • In boolean contexts, a stream is converted to a value of type void*

  • Formatting flags - class ios_base

    • For white space skipping, ajustment, padding, integer base, floating point formatting, etc.

    • The formatting flags can be read and written together (p. 626)

Program: Illustration of implicit stream conversion to a boolean value.
#include <iostream>
#include <string>

using namespace std;

int main(){
  int i;

  while (cin >> i)   // cin is converted to 'a boolean value'
                     // via an implicit conversion operator
     cout << 2*i << " ";
  cout << "The End";

}

At a higher level of abstraction, the formatting flags are changed by so-called manipulators - see next page

Manipulators
Slide Annotated slide Contents Index
References 

A manipulator is a distinguished operand accepted by the << and >> IO operators

A manipulator mutates the state of a stream

Reference
  • The C++ Programming Language: Page 631

Program: Output manipulators.
  cout << 17 << endl << showbase 
       <<  hex << 17 << endl
       << oct << 17 << endl;

Program: The fragment in a complete program.
#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

int main(){

  cout << 17 << endl << showbase 
       <<  hex << 17 << endl
       << oct << 17 << endl;   
}

Program: Program output.
17
0x11
021

More manipulators
Slide Annotated slide Contents Index
References 

Manipulators can be with or without arguments

  • Manipulators without arguments

    • The manipulator is a pointer to function

    • Handled by an operator overload of operator<< in class basis_ostream

      • basis_ostream operator<<(basis_ostream& (*f)(basis_ostream&)){return f(*this)}

  • Manipulators with arguments

    • The manipulator m(a) returns an struct that aggregates a function pointer and the argument a

    • An overload of << activates the function on the argument before it returns the stream

    • Interesting details on page 633

Program: Illustration of more manipulators.
#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

int main(){
  double d = 3.12345678;

  cout << d << endl 
       << scientific << d << endl 
       << fixed
       << setprecision(10) << setfill('#') << setw(18)
       << d << endl;
}

Program: Program output.
3.12346
3.123457e+00
######3.1234567800

Logical program organization
Slide Annotated slide Contents Index
References 

Namespaces are used for the logical program organization of C++ programs

Reference
  • The C++ Programming Language: Page 167, 847

  • Namespaces

    • A namespace is a named scope

    • Usages of names require namespace qualification

      • namespace::name_within_namespace

    • A using declaration can be used to add a name (an alias) to a local scope (such as a function)

      • using N::name;

      • name is declared/defined as a local synonym to N::name

    • A using directive allows convenient access to names from a given namespace

      • using namespace N;

      • Can - and should - be used locally, in a function for instance

Names declared in a namespace - natively or by using declarations - take priority over names brought in via using directives

Reference

Program: Illustration of using declarations and using directives.
// Reproduced from page 847 of 'The C++ Programming Language'.

#include <iostream>
#include <string>

namespace X {
  int i = 10, j = 11, k = 12;
}

int k;                // Global variable

void f1(){            // Illustrates using directives
  int i = 0;
  using namespace X;  // A using directive makes i, j, and k from X accessible
  i++;                // local i
  j++;                // X::j
  k++;                // X::k or ::k   reference to k is ambiguous
  ::k++;              // global k
  X::k++;             // X::k
}

void f2(){            // Illustrates using declarations
  int i = 0;
  using X::i;         // double declaration: i already declared in this scope
  using X::j;       
  using X::k;         // hides global k

  i++;                // local i
  j++;                // X::j
  k++;                // X::k
}

int main(){
  f1();
  f2();
}

More namespaces
Slide Annotated slide Contents Index
References 

Oher namespace characteristics

  • A namespace is open

    • A single namespace N can span several source files

    • For instance namespace N {...} forms in two or more header files

  • Namespaces can be nested

  • There is a global namespace

    • Access to name in the global namespace:     ::name

  • Unnamed namespaces

    • Used in C++ to make names local to a compilation unit

Reference
  • The C++ Programming Language: Page 177, 200 - Unnamed namespaces

Program: Illustrating that a namespace interface is separated from the namespace implementation.
// First we see a namespace interface followed
// by definition/implementation of the functions
// in the namespace.

namespace N{          // The namespace interface
  int f(int i);
  double g();
}

int N::f(int i){      // The namespace implementation
  return 2*i;
}

double N::g(){
  return f(2);
}


int main(){
  using N::f; 

  int r = f(5);
  double d = N::g();
}

The syntactic namespace form is intended for declarations, not full definitions

This separates the namespace interface from the namespace implementation

In the same way as for classes

Physical program organization
Slide Annotated slide Contents Index
References 

The physical program organization is done by means of source files

Reference
  • The C++ Programming Language: Page 197 - ...

  • Source file organization of C++ programs

    • Source files with #include directives

    • A translation units is the result of preprocessing a source file

    • .h files and .cpp files

      • The .cpp file includes its .h file.

    • Separate compilation - and subsequent linking

    • External linkage:

      • A name that can be used in other translation units than the one where it is defined

    • Internal linkage

      • A name that only can be used in the translation unit where it is defined

      • Declared as static in C programs and older C++ programs

      • Use an unnamed namespace for internal linkage in C++ programs

Reference
  • The C++ Programming Language: Page 199: External and internal linkage

Example of program organization
Slide Annotated slide Contents Index
References 

Two header files point.h and tripple.h

Class Point and Tripple depends mutually on each other

Two cpp files point.cpp and tripple.cpp

A main program file main.cpp

Program: The header file point.h.
#ifndef POINT_H
#define POINT_H

#include "tripple.h"

class Tripple;

class Point {
    private: 
        double x, y;

    public:
        Point();                     
        Point(double d);             
        Point(double d, double e);   
        Point(Point& p);             
        Point(Tripple t);

        operator double() const;     

        double getx () const;
        double gety () const;
};

std::ostream& operator<<(std::ostream&, const Point&);

#endif // POINT_H

Program: The header file tripple.h.
#ifndef TRIPPLE_H
#define TRIPPLE_H

#include "point.h"

class Point;

class Tripple {
    private:
        int x, y, z;

    public:
        Tripple();                   
        Tripple(int xi, int yi, int zi); 
        Tripple(Point p);             

        int getx() const;
        int gety() const;
        int getz() const;

        friend std::ostream& operator<<(std::ostream&, const Tripple&);
};

std::ostream& operator<<(std::ostream&, const Tripple&);

#endif // TRIPPLE_H

Program: The header file point.cpp.
#include <cmath>
#include <iostream>
#include "point.h"

Point::Point(): x(0.0), y(7.0){
}

Point::Point(double x_coord): x(x_coord), y(20.0){
}

Point::Point(double x_coord, double y_coord): x(x_coord), y(y_coord){
}

Point::Point(Point& p): x(p.x + 1.0), y(p.y + 2.0){
}

Point::Point(Tripple t): x(double(t.getx())), y(double(t.gety())) {
}

double Point::getx () const{
    return x;
}
double Point::gety () const{
    return y;
}

std::ostream& operator<<(std::ostream& s, const Point& p){
    return s << "(" << p.getx() << "," << p.gety() << ")" ;
}

Program: The header file tripple.cpp.
#include <cmath>
#include <iostream>
#include "tripple.h"


Tripple::Tripple(): x(0), y(0), z(0){
}

Tripple::Tripple(int xi, int yi, int zi): x(xi), y(yi), z(zi){
}

Tripple::Tripple(Point p): x(int(p.getx())), y(int(p.gety())), z(0){
}

int Tripple::getx() const {
    return x;
}

int Tripple::gety() const {
    return y;
}

int Tripple::getz() const {
    return z;
}

std::ostream& operator<<(std::ostream& s, const Tripple& tr){
  return s << "[" << tr.x << "," << tr.y << "," << tr.z << "]";
}

Program: The header file main.cpp.
#include <iostream>

#include "point.h"
#include "tripple.h"

int main() {
    using namespace std;

    Point p, q(3,4), r(Tripple(1,2,3));
    Tripple t, v(p);

    cout << "p: "<< p << endl;   // funny default constructor: (0,7)
    cout << "q: "<< q << endl;   // (3,4)
    cout << "r: "<< r << endl;   // (1,2)

    cout << "t: "<< t << endl;   // (0,0,0)
    cout << "v: "<< v << endl;   // (0,7) -> (1,9) per Point copy constructor -> (1,9,0)
    
    return 0;
}

Program: Compilation - README.
Compilation:
  g++ -c point.cpp
  g++ -c tripple.cpp
  g++ point.o tripple.o main.cpp

I have added 'Include Guards' in the two header files.

point.h includes tripple.h
tripple.h includes point.h
class Point is forward declared in tripple.h
class Tripple is forward declared in point.h

A cpp file includes its header file - should always happen.
  Thus: point.cpp includes point.h
        tripple.cpp includes tripple.h

main.cpp includes both point.h and tripple.h

This is a nice and symmetric solution - as I see it.

The standard library namespace
Slide Annotated slide Contents Index
References 

All facilities in the C++ Standard Library are defined in the std namespace

  • Namespace resolution

    • Use the scope operator :: on every name you use from the standard name space

      • std::cout << s1 << std::endl;

      • Allows for symmetric use of the same names from other namespaces

      • Tedious in short and simple programs

    • Apply using declarations on selected names from the standard namespace

      • using std::cout; using std::endl;

      • Tedious, just in another way...

    • Apply a single using directive

      • using namespace std;

      • All names from the standard namespace can be used without qualifiction

      • OK for namespace composition - OK in certain local scopes

      • Sloppy and lazy if used in the global namespace

Reference
  • The C++ Programming Language: Page 179: Namespace composition

Point Exercise - C++ versus C#
Slide Annotated slide Contents Index
References 

This slide contains programs intended for an exercise about similarities and differences between C++ / C# object allocation, access, and parameter passing

Program: The C# Point class.
using System;

public class Point {
  private double x, y;

  public Point(double x, double y){
   this.x = x; this.y = y;
  }

  public double Getx (){
    return x;
  }

  public double Gety (){
    return y;
  }

  public void Move(double dx, double dy){
    x += dx; y += dy;
  }

  public double DistanceTo(Point p){
    return Math.Sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
  }

  public override string ToString(){
    return "Point: " + "(" + x + ", " + y + ")" + ". ";
  }
}

Program: A C# client class of class Point.
using System;

public class PointClient{

  public static void Main(){
    PointClient pc = new PointClient();
    pc.Go();
  }

  public void Go(){
    Point p1 = new Point(1, 2),
          p2 = new Point(3, 4),
          p3 = p1,
          p4 = p2,
          p5;

    p5 = PointMover(p1, p2);

    Console.WriteLine("{0} {1} {2} {3} {4}", 
       p1, 
       p2, 
       p3, 
       p4, 
       p5  
     );
  }

  public Point PointMover(Point pa, Point pb){
    pa.Move(5,6);
    pb.Move(-5,-6);
    return pb;
  }

}

Program: A C# client class of class Point - with comments that reveal the output.
using System;

public class PointClient{

  public static void Main(){
    PointClient pc = new PointClient();
    pc.Go();
  }

  public void Go(){
    Point p1 = new Point(1, 2),
          p2 = new Point(3, 4),
          p3 = p1,
          p4 = p2,
          p5;

    p5 = PointMover(p1, p2);

    Console.WriteLine("{0} {1} {2} {3} {4}", 
       p1,  // (6,8)
       p2,  // (-2,-2)
       p3,  // (6,8)      an alias of p1
       p4,  // (-2,-2)    an alias of p2
       p5   // (-2,-2)    another alias of p2
     );
  }

  public Point PointMover(Point pa, Point pb){
    pa.Move(5,6);
    pb.Move(-5,-6);
    return pb;
  }

}

Exercise 2.7. Use of class Point in C++

In this exercises we will explore some basic similarities and differences between use of classes in Java/C# and C++. (In the scope of this lecture we will NOT be concerned with class definition details. This belongs to the next lecture). In particular we will study creation/allocation of objects, and parameter passing of objects to a function/method.

The starting point is the simple Point class in C# and a sample C# client program that creates and operates on points. Make sure that you familiarize yourself with the correct output of this program. Predict it before you run it.

We provide a similar Point class in C++. The C++ definitions of the Point functions are also provided (but they are not important for this exercise). You are supposed to explore various uses of class Point in C++.

  1. Write a C++ client program that corresponds as close as possible to the C# client program. In this version of the C++ program you should access Point objects via pointers. First, write a version where you allocate the points in the free store.
  2. Adjust the C++ program from above such that the points are allocated on the stack, with pointers established to these (by use of the address operator).
  3. Write another version of the C++ client program uses C++ references instead of pointers. The goal is to come as close as possible to the pointer-based program you wrote above.
  4. Finally, write a C++ client program that use pure and simple value semantics of points. In other words, do not use references nor pointers at all in this version of the C++ program. We accept that the meaning of the program changes due to the introduction of value semantics. Predict the output of the program before you run it.

Feel free to do other experiments based on the Point classes that help you understand the similarities and differences between use of C# and C++ classes, pointers, references, and stack allocation of C++ objects.

Program: The C++ Point class - a header file.
class Point {
private: 
  double x, y;

public:
  Point(double, double);
  Point();
  double getx () const;
  double gety () const;
  void move(double, double);
  double distance_to(Point);
};

std::ostream& operator<<(std::ostream&, const Point&);

Program: The C++ Point class - the cc file.
#include <cmath>
#include <iostream>
#include "point.h"

Point::Point(double x_coord, double y_coord): x(x_coord), y(y_coord){
}

Point::Point(): x(0.0), y(0.0){
}

double Point::getx () const{
  return x;
}

double Point::gety () const{
  return y;
}

void Point::move(double dx, double dy){
    x += dx; y += dy;
}

double Point::distance_to(Point p){
    return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}

std::ostream& operator<<(std::ostream& s, const Point& p){
  return s << "(" << p.getx() << "," << p.gety() << ")" ;
}

Program: The C++ client program with pointers - free store allocation.
#include <iostream>
#include "point.h"

using namespace std;

Point *pointmover(Point *pa, Point *pb){
  pa->move(5,6);
  pb->move(-5,-6);
  return pb;
}

void go(){
  Point *p1 = new Point(1, 2),
        *p2 = new Point(3, 4),
        *p3 = p1,
        *p4 = p2,
        *p5;

    p5 = pointmover(p1, p2);

    cout << *p1 << endl <<    // (6,8)
            *p2 << endl <<    // (-2,-2)
            *p3 << endl <<    // (6,8)
            *p4 << endl <<    // (-2,-2)
            *p5 << endl;      // (-2,-2)
}

int main(){
  go();
}

Program: The C++ client program with pointers - stack allocation.
#include <iostream>
#include "point.h"

using namespace std;

Point *pointmover(Point *pa, Point *pb){
  pa->move(5,6);
  pb->move(-5,-6);
  return pb;
}

void go(){
  Point p(1,2),
        q(3,4),
        *p1 = &p,
        *p2 = &q,
        *p3 = p1,
        *p4 = p2,
        *p5;

    p5 = pointmover(p1, p2);

    cout << *p1 << endl <<    // (6,8)
            *p2 << endl <<    // (-2,-2)
            *p3 << endl <<    // (6,8)
            *p4 << endl <<    // (-2,-2)
            *p5 << endl;      // (-2,-2)
}

int main(){
  go();
}

Program: The C++ client program with references.
#include <iostream>
#include "point.h"

using namespace std;

Point &pointmover(Point &pa, Point &pb){
  pa.move(5,6);
  pb.move(-5,-6);
  return pb;
}

void go(){
  Point p1(1, 2),
        p2(3, 4),
        &p3 = p1,
        &p4 = p2;

  Point &p5 = pointmover(p1, p2);

  cout << p1 << endl <<   // (6,8)
          p2 << endl <<   // (-2,-2)
          p3 << endl <<   // (6,8)
          p4 << endl <<   // (-2,-2)
          p5 << endl;     // (-2,-2)
}

int main(){
  go();
}

Program: The C++ client program with Point value semantics.
#include <iostream>
#include "point.h"

using namespace std;

Point pointmover(Point pa, Point pb){
  pa.move(5,6);
  pb.move(-5,-6);
  return pb;
}

void go(){
  Point p1(1, 2),
        p2(3, 4),
        p3 = p1,
        p4 = p2;

  Point p5 = pointmover(p1, p2);

  cout << p1 << endl <<   // (1,2)
          p2 << endl <<   // (3,4)
          p3 << endl <<   // (1,2)
          p4 << endl <<   // (3,4)
          p5 << endl;     // (-2,-2)
}

int main(){
  go();
}


Collected references
Contents Index
Fundamental types in C
The C++ Programming Language: Page 71
The C++ Programming Language: Page 234
The C++ Programming Language: Page 78
The C++ Programming Language: ODR. Page 203
The C++ Programming Language: Page 79 - 80
The C++ Programming Language: Page 80
The C++ Programming Language: Page 133, 135
The C++ Programming Language: Page 94 - 97
The C++ Programming Language: Page 84
The C++ Programming Language: Page 90
The C++ Programming Language: Page 48-49, 579
The C++ Programming Language: Page 588
String dokumentation at cplusplus.com
The C++ Programming Language: Page 97
Objects
The C++ Programming Language: Argument passing. Value return. Page 145-148
The C++ Programming Language: Page 146
The C++ Programming Language: Page 98, 146, 148, 283
The C++ Programming Language: Page 148
The C++ Programming Language: Page 130-131, 408, 819
The C++ Programming Language: Page 833
The C++ Programming Language: Page 130-131, 407-408 (dynamic casts), 819
The C++ Programming Language: Page 149-153
The C++ Programming Language: Page 149
The C++ Programming Language: Page 52, 442
Vector dokumentation at cplusplus.com
Arrays and pointers
Array examples
The C++ Programming Language: Page 127
The C++ Programming Language: Page 607, 613
The C++ Programming Language: Page 121-122: operator priorities
The C++ Programming Language: Page 609, 614
The C++ Programming Language: Page 616
The C++ Programming Language: Page 631
The C++ Programming Language: Page 167, 847
Classes and structs
The C++ Programming Language: Page 177, 200 - Unnamed namespaces
The C++ Programming Language: Page 197 - ...
The C++ Programming Language: Page 199: External and internal linkage
The C++ Programming Language: Page 179: Namespace composition

 

Chapter 2: Basic facilities
Course home     Author home     About producing this web     Previous lecture (top)     Next lecture (top)     Previous lecture (bund)     Next lecture (bund)     
Generated: March 26, 2013, 13:03:15