Exercise index of this lecture   Alphabetic index   Course home   

Exercises and solutions
Basic facilities, Part 1


2.1   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.


2.2   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.

Please be aware that the cases become increasingly strange...

  1. Make a version with C-style call by reference parameters - pointers passed by value.
  2. Make a version with call by C++ references. Also, return a double&.
  3. Make a version with call by C++ const references instead of call by value parameters.
  4. Demonstrate that pointers to references do not exist.
  5. Make a version with references to pointers. Does that make sense to you?
  6. Next, make version with const references to pointers.
  7. Finally, a version with const references to pointers to double constants

Can you imagine other interesting variations?

Solution

Here are my variations of the max function:

#include <iostream>
#include <string>

// By value
double max0(double a, double b){
  return a < b ? b : a;
}

// By pointers - C-style 'call-by-reference'
double max0_ptrs(double* a, double* b){
  return *a < *b ? *b : *a;
}

// By pointers - C++ style references
double& max0_refs(double& a, double& b){
  return a < b ? b : a;
}

// By const reference
const double& max1(const double& a, const double& b){
  return a < b ? b : a;
}

// Pointers to references - DOES NOT COMPILE. Pointers to references do not make sense.
const double& max2(double& *a, double & *b){ 
   return *a < *b ? *b : *a;
}

// References to pointers
double max3(double* &a, double* &b){     
  return *a < *b ? *b : *a;
}

// Const references to pointers
const double& max4(double* const &a, double* const &b){    
  return *a < *b ? *b : *a;
}

// const reference to const double pointer  (it is the doule which is constant).
const double& max5(const double* const &a,const double* const &b){     
  return *a < *b ? *b : *a;
}

int main(){
  using namespace std;

  double a = 1, b = 2;
  double *c = &a, *d = &b;
  const double e = 3, f = 4;

  cout << max0(a, b) << endl;         // Call by value

  cout << max0_ptrs(c, d) << endl;    // Call by C-style references - pointers by value

  cout << max0_refs(a, b) << endl;    // Call by C++ -style references. Returns a Lvalue reference. 
                                      // The assignment max0_refs(a, b) = 3;  will change the value of b.

  cout << max1(a, b) << endl;         // Call by C++ const reference

  cout << max3(c, d) << endl;         // Call by C++ reference to pointers

  cout << max4(&a, &b) << endl;

  cout << max4(c, d) << endl;

  cout << max4(&e, &f) << endl;     // ERROR: Invalid conversion from 'const double*' to 'double*'

  cout << max5(&e,&f) << endl; 
}


2.3   Contrasting use of C# and C++ classes: Class Point  

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 a forthcoming 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. On purpose, I do not use C# properties - but simple accessor methods (ala Java). 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 to objects on the free store (the heap). You may want to read a bit about the free store and new in C++.
  2. Adjust the program such that the Point objects are allocated on the stack, and accessed via pointers (Use the address operator to get the pointers). Does this affect the need for deallocation?
  3. Write another version of the C++ client program that 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.

Solution

Here follows a number of programs that illustrate the issues, which are brought up in this exercise.

1. 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)

  delete p1;
  delete p2;
}

int main(){
  go();
}


 

2. The C++ client program with pointers - stack allocation. Notice that the free store is not used, and therefore there is no need for calling delete.

#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)

  // Nothing to deallocate with delete!
}

int main(){
  go();
}


 

3. 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();
}


 

4. The C++ client program with Point value semantics

// Notice: New sematics due to use of call-by-value.

#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,    // Copy p1
        p4 = p2;    // Copy p2

  Point p5 = pointmover(p1, p2);   // Pass copies. 

  cout << p1 << endl <<   // (1,2)
          p2 << endl <<   // (3,4)
          p3 << endl <<   // (1,2)
          p4 << endl <<   // (3,4)
          p5 << endl;     // (-2,-2), a moved copy of p2, copied back from pointmover.
}

int main(){
  go();
}


 


Generated: Tuesday August 1, 2017, 13:25:57