-


> > > > Examples and Notes > Final Exam Practice

Problem 0: Review old programming assignments, lectures, readings, and exams. Use Piazza to suggest a question you feel is missing from this collection of practice problems.


Problem : Give a brief description of the purpose of the following object-oriented and architectural design patterns:


Problem : For the following class definitions, which statements in main are legal and what is the result of the statements that are legal?

Solution: code and output

#include <iostream>
#include <memory>

class Base
{
public:
  virtual void kick() = 0;
  virtual void pass() { std::cout << "Base::pass" << std::endl; }
  void run() { std::cout << "Base::run" << std::endl; }
private:
  int x;
};

class DerivedA : public Base
{
public:
  void kick() { std::cout << "DerivedA::kick" << std::endl; }
  void pass() { std::cout << "DerivedA::pass" << std::endl; }
  void punt() { std::cout << "DerivedA::punt" << std::endl; }
};

class DerivedB : public Base
{
public:
  void kick() { std::cout << "DerivedB::kick" << std::endl; }
  void punt() { std::cout << "DerivedB::punt" << std::endl; }
  void run() { std::cout << "DerivedB::run" << std::endl; }
private:
  int y;
};

class DerivedC : public DerivedA
{
public:
  void kick() { std::cout << "DerivedC::kick" << std::endl; DerivedA::kick(); }
  void run() { std::cout << "DerivedC::run" << std::endl; }
};

class DerivedD : private Base
{
public:
  void kick() { std::cout << "DerivedD::kick" << std::endl; }
  void punt() { std::cout << "DerivedD::punt" << std::endl; }
};

int main(int argc, char **argv)
{
  // Which of the following are legal, and for legal statements what is the output?
  std::shared_ptr<DerivedA> one = std::make_shared<DerivedA>();
  std::shared_ptr<DerivedB> two = std::make_shared<DerivedB>();
  std::shared_ptr<Base> three = std::make_shared<DerivedC>();
  std::shared_ptr<DerivedD> four = std::make_shared<DerivedD>();
  DerivedA five = DerivedC();

  one->kick();
  one->pass();
  one->run();
  one->punt();
  
  two->kick();
  two->pass();
  two->run();
  two->punt();

  three->kick();
  three->pass();
  three->run();
  three->punt();

  four->kick();
  four->pass();
  four->run();
  four->punt();

  five.kick();
  five.pass();
  five.run();
  five.punt();

  // Which of the following cause compiler errors, and what is the result of the ones that do not?
  std::shared_ptr<Base> v = std::make_shared<DerivedD>();
  std::shared_ptr<Base> w = std::make_shared<Base>();
  std::shared_ptr<DerivedC> x = one;
  std::shared_ptr<DerivedA> y = three;
  std::shared_ptr<DerivedB> z = std::dynamic_pointer_cast<DerivedB>(three);
  
  return 0;
}

Problem : Given the code below, draw the layout of an instance of Derived, including the vtables.

class Base
{
public:
  virtual ~Base() {}
  virtual void kick() = 0;
  virtual void pass() {}
  void run() {}
private:
  int x = 1;
};

class MidA : public Base
{
public:
  void kick() {}
  void pass() {}
  virtual void punt() {}
};

class MidB : public Base
{
public:
  void kick() {}
  virtual void punt() {}
  void run() { punt(); }
private:
  int y = 2;
};

class Derived: public MidA, public MidB
{
public:
  virtual void punt() {}
  virtual void fumble() {}
private:
  int z = 3;
};

int main(int argc, char **argv)
{
  Derived d;
  d.MidB::run();
}

Problem : Which of w, x, y, and z are legal to use in the zero methods below?

#include <iostream>

class A
{
  int w;
public:
  int x;
protected:
  int y;
private:
  int z;

  void zero(A* a) { a->___ = 0; } 
};

class B : protected A
{
  void zero(A* a) { a->___ = 0; } 
  void zero(B* b) { b->___ = 0; } 
};

class C : public B
{
  void zero(B* b) { b->___ = 0; } 
  void zero(C* c) { c->___ = 0; } 
};

class D
{
  void zero(A* a) { a->___ = 0; } 
};

Problem : Refactor the following classes so they are both derived from a base class called Dwelling. Write the code for the new base class as well. Try to avoid repeating code as much as possible. Write the classes so that any common methods can be invoked through a pointer or reference to the base class.

Solution

#include <string>
#include <cmath>

namespace cs427_527
{
  class House
  {
  public:
    House(int sz, std::string addr, int t);
    int getTaxes() const;
    double estimateUtilities() const;
    std::string getAddress() const;
  private:
    int size;
    int taxes;
    std::string address;
  };
  
  House::House(int sz, std::string addr, int t)
    : size(sz), address(addr), taxes(t)
  {
  }

  int House::getTaxes() const
  {
    return taxes;
  }

  double House::estimateUtilities() const
  {
    return size * size / std::exp(size / 3);
  }

  std::string House::getAddress() const
  {
    return address;
  }

  class Apartment
  {
  public:
    Apartment(int sz, std::string addr);
    double estimateUtilities() const;
    std::string getAddress() const;
  private:
    int size;
    std::string address;
  };

  Apartment::Apartment(int sz, std::string addr)
    : size(sz), address(addr)
  {
  }

  double Apartment::estimateUtilities() const
  {
    // original version of this was not as interesting
    return size * size / std::exp(size / 3) - size / 10;
  }

  std::string Apartment::getAddress() const
  {
    return address;
  }

}

Problem : Explain the problems with converting this to a smart pointer as in the example below.

#include <memory>

template <typename T>
void multiPress(std::shared_ptr<T> b, int count)
{
  for (int i = 0; i < count; i++)
    {
      b->press();
    }
}

class Button
{
public:
  Button() : count(0) {}
  void press() { count++; }
  void reset() { multiPress(std::shared_ptr<Button>(this), 3); count = 0; }
  
private:
  int count;
};
    

int main(int argc, char **argv)
{
  std::shared_ptr<Button> b = std::make_shared<Button>();
  b->reset();
}

Problem : Compare and contrast the available features and their implementation in C++, Java, and Go for inheritance and generic programming.


Valid HTML 4.01 Transitional