Final Exam Review#

Note

The final exam will cover the topics since the previous exam.

Python will be part of the exam, however, there will not be questions on NumPy and matplotlib, since we did not have enough time to practice those.

Important

This set of examples is not exhaustive, so you should go back and read through the notes, run the example codes, and look at my homework solutions.

File I/O#

  1. You create a file as:

    std::ofstream of("file.txt");
    
    1. How do you output a string to it?

      solution

      You simply use the output stream operator, <<:

      std::string s{"hello"};
      of << s;
      
    2. If the file already exists, what happens to its existing contents when you open it this way?

      solution

      The contents are overwritten. To append to the file, see the discussion at the end of File I/O.

    3. How would you change this line if you wanted to read from file.txt?

      solution

      You would need to create a std::ifstream—this is the input stream.

Standard Library Algorithms#

  1. Given a std::vector<int> v{2, 5, 8, 5, 10};, write one line using std::ranges::count to count how many times 5 appears.

    solution
    auto num = std::ranges::count(v, 5);
    

    This will look over the entire vector and count how many 5 it contains.

  2. What does v.end() point to? Why should an iterator loop usually stop at it < v.end() or it != v.end() instead of including v.end()?

    solution

    v.end() points to one-past the end of the vector.

  3. Use std::ranges::find to search for 8 in a vector. What should you check before dereferencing the returned iterator?

    solution

    We should check if it is pointing to v.end(), like:

    bool found{};
    auto it = std::ranges::find(v, 8);
    if (it != v.end()) {
         found = true;
    }
    
  4. Write a lambda that returns true if an integer is odd. Then use it with std::ranges::count_if.

    solution

    Assuming our vector is called v, we can do:

    auto num = std::ranges::count_if(v, [] (int e) {return e % 2 != 0;});
    

    The lambda-function here, [] (int e) {return e % 2 != 0;}, captures nothing from the surrounding scope ([]), takes a single int e as the argument, and then checks the modulus of e with 2 to determine if it is odd. The return value here is bool, which C++ can determine automatically.

  5. For std::views::iota(3, 8), what values are produced?

    solution

    This produces the sequence of integers that starts at 3 and goes up to, but not including 8, so it would result in 3, 4, 5, 6, 7. This behavior is identical to python’s range() function.

  6. Suppose an iterator pos points to the element 200 in a vector. What does v.insert(pos, 150) do?

    solution

    This will insert the number 150 into the vector v just before the element 200.

  7. In this example, we use a lambda function. If we instead wanted to use a traditional function, how would we rewrite this (show both the new function and the updated sort call).

    std::ranges::sort(titles,
                      [] (const std::string& a, const std::string& b)
                         {return a.size() < b.size();});
    
    solution

    We would first write a function that returns a bool, like:

    bool compare(const std::string& a, const std::string& b) {
        return a.size() < b.size();
    }
    

    and then our call would look like:

    std::ranges::sort(titles, compare);
    

Software Engineering / Version Control#

  1. These lines are in a GNUmakefile:

    planet_sort_split.o: planet_sort_split.cpp planet.H
         g++ -std=c++20 -c planet_sort_split.cpp
    
    1. In the first line, what does the name to the left of the : mean? and what are the files to the right of the :?

      solution

      The name before the : is the target—it is what we want to produce. The list of files after the : are the dependencies—those are the files we need in order to make the target.

    2. What does the -c do in the g++ command?

      solution

      This simply compiles the file without linking it to make an executable. It produces an object file (planet_sort_split.o in this case).

      In general, we do this on files that don’t contain a main function, and then once all the object files are created, we link them together to make the executable.

  2. If I compile a program.cpp as:

    g++ -O3 -o program program.cpp
    

    what is the meaning of -O3?

    solution

    This enables optimization (and in this case, a high-level of optimization). The resulting code can run much faster than unoptimized code.

  3. git status shows newton.cpp under Untracked files—how do you put this file under git control?

    solution

    We need to add it to our git repository, by doing:

    git add newton.cpp
    
  4. What does it mean for a variable to be shadowing another variable in C++?

    solution

    This means that we created an object with the same name as one in the surrounding scope. This may not be a problem, but if we think that we are actually working on the version of the object in the outer scope, then our local copy will shadow that and any changes we make to it will not be reflected in the outer scope.

  5. We have a header solver.H in our current directory. This header provides a function newton in it. We want to use this in our code. How do we include this header so the compiler will find it.

    solution

    You need to use " in the #include, like:

    #include "solver.H"
    

    See Working with Multiple Files.

Classes#

  1. What is the main difference between a struct and a class in C++?

    solution

    The default accessibility of member data and functions. With class, everything is private by default, so we need to explicitly make it public. With struct, everything is public by default.

  2. What is a constructor, and when is it called? What name is given to the constructor function?

    solution

    The constructor is the special member function that is called when we create an object / instance of our class. The constructor function has the same name as the class.

  3. What is an initialization list? In the code below, what member data is initialized?

    SolarSystem(double mass)
        : star_mass(mass)
    {}
    
    solution

    Here, the initialization list is the : star_mass(mass) following the constructor function’s arguments. It initializes member data as the object is created, and before the body of the constructor function is executed.

    In this example, we are initializing the member data star_mass.

  4. Consider the following:

    struct Circle {
    
        // member data
        double radius{};
    
        // constructor
        Circle(double r) {
            radius = r;
        }
    };
    
    1. How would you create a Circle with a radius of 5?

      solution
      Circle c(5);
      
    2. Rewrite this to make the member data private and add a getter function that returns the radius.

      Show how you would call this function given a Circle object c.

      solution
      class Circle {
      
      private:
      
          // member data
          double radius{};
      
      public:
      
          // constructor
          Circle(double r) {
              radius = r;
          }
      
          // getter
          double r() {
              return radius;
          }
      };
      

      We would call this as:

      auto radius = c.r();
      
    3. How would you add another constructor that takes no arguments and sets the radius to 1 by default?

      solution

      The second constructor would have the same name, but a different argument list (so C++ can tell them apart). In this case, it would take no arguments:

      Circle() {
          radius = 1;
      }
      
  5. In our Vector2d class, we had 2 different operators for multiplying by a scalar:

    Vector2d operator*(double a) {
        return Vector2d(x * a, y * a);
    }
    
    friend Vector2d operator*(double a, const Vector2d& v);
    

    What is the difference between these?

    solution

    The first is a member function, and for a Vector2d named v and a scalar a, it would be invoked when our Vector2d is to the left of the multiplication operator, i.e. v * a.

    The second is a friend. The function itself is outside of the class, so it is not a member function. This is involved when our class is to the right of the operator, i.e., a * v.

  6. Write a class Timer with private member data seconds and a member function add_time(double dt) that adds dt to the stored seconds, and another member function, get_time() that returns the stored time.

    solution
    class Timer {
    
        double seconds;
    
    public:
    
        Timer()
            : seconds{}
        {}
    
        void add_time(double dt) {
            seconds += dt;
        }
    
        double get_time() {
            return seconds;
        }
    };
    
  7. Currency exchange.

    1. Create a class called Currency that holds a double and a string. The double is the value and the string is the country of the currency (e.g., "US", "Euro", …). Give a constructor that takes both pieces of data.

      solution
      class Currency {
      
          double value;
          std::string country;
      
      public:
      
          Currency(double v, std::string c)
              : value(v), country(c)
          {}
      };
      
    2. We want to be able to add two Currency objects. The operator function will look like:

      Currency operator+ (const Currency& other)
      

      Fill in the details—include a check that we are adding currencies from the same country.

      solution

      The operator function would look like:

      Currency operator+ (const Currency& other) {
          if (country != other.country) {
              std::cout << "Error: countries do not match" << std::endl;
              std::exit(1);
          }
          return Currency(value + other.value, country);
      }
      

Python#

  1. What is the python version of a C++ std::vector that we focused on in class?

    solution

    A list. In python we create a list using [].

  2. Given the following:

    a = [1, 2, 4, 8, 16]
    

    write a loop (in python) that loops over the elements and prints them out.

    solution
    for e in a:
        print(e)
    
  3. Some times in class, I put an f in front of the " in a string—what does this mean?

    solution

    This is a f-string or format-string. It uses similar formatting codes as C++ std::format, but also allows us to put the object we are outputting inside that {}, before the :.

  4. How would I write this C++ if-test in python?

    int sign;
    if (x == 0) {
        sign = 0;
    } else if (x > 0) {
        sign = 1;
    } else {
        sign = -1;
    }
    
    solution
    if x == 0:
        sign = 0
    elif x > 0:
        sign = 1
    else:
        sign = -1
    

    Notice that in python we don’t need to declare our sign outside of the if-block—scoping works differently in python.

  5. What is the python equivalent of this C++ code

    double x{2.5};
    double y{1.2};
    
    double z = std::pow(x, y);
    
    solution

    We can use the ** operator, so we can do:

    x = 2.5
    y = 1.2
    z = x**y
    
  6. What is the result of the following python code:

    def func(x, normalization=None):
        xi = x
        if normalization:
            xi = x / normalization
    
        return xi**3 + xi + 1
    
    func(2, normalization=2)
    
    solution

    We are using the keyword argument normalization, so inside the function, normalization will have the value 2. So xi will be 1 in the function (2 / 2), and we get 3 as the result.

  7. This was the first class we wrote in C++:

    struct Circle {
    
        // member data
        double radius{};
    
        // constructor
        Circle(double r) {
            radius = r;
        }
    
        // member functions
        double circumference() {
            return 2.0 * std::numbers::pi * radius;
        }
    
        double area() {
            return std::numbers::pi * radius * radius;
        }
    
    };
    

    write a python version of it.

    solution
    class Circle:
        def __init__(self, r):
            self.radius = r
    
        def circumference(self):
            return 2.0 * math.pi * self.radius
    
        def area(self):
            return math.pi * self.radius**2
    

    In python, the constructor is __init__ and all member functions (or methods in python lingo) have self as the first argument.