renaming labs
This commit is contained in:
89
labs/exceptions_inheritance/README.md
Normal file
89
labs/exceptions_inheritance/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Lab 13 — Multiple Inheritance & Exceptions
|
||||
|
||||
For this lab you will build a class inheritance structure to match the hierarchy of classic geometric shapes.
|
||||
The finished program will read lists of 2D point coordinates from a file and determine the shape described
|
||||
by each list of points. We will use a somewhat quirky method to determine the type of each shape. We will
|
||||
pass the list of points to each specialized shape constructor in turn, and if the constructor doesn’t fail, then
|
||||
we know that that list of points is in fact that type of shape. Remember, the only way for a constructor to
|
||||
fail is to throw an exception.
|
||||
|
||||
## Checkpoint 1: Shape Hierarchy
|
||||
|
||||
*estimate: 20-40 minutes*
|
||||
|
||||
Consider the “is-a” relationships between these 13 different shapes: Polygon, Triangle, Quadrilateral, Isosceles Triangle, Right Triangle, Isosceles Right Triangle, Equilateral Triangle, Trapezoid, Kite, Parallelogram, Rectangle, Rhombus, and Square. Note that a particular shape may be correctly labeled by more than one of these names; e.g., a Square is also a Quadrilateral.
|
||||
|
||||
Draw the class hierarchy with arrows indicating all of the inheritance relationships (you do not need to include the member variables or member functions). Be neat, have a consistent (up or down) orientation to your arrows, and avoid messy scribbles or cross outs and arrow crossings. Sketch the shapes in the [input.txt](cp1/input.txt) file - each line of the input file begins with a string name followed by 3 or more 2D coordinate vertices. Write the name of each shape next to the most specific type of shape it represents. Hint: you may want to grab or print yourself some graph paper.
|
||||
|
||||
The inheritance diagram of these shapes includes multiple inheritance, specifically in the form of the Diamond Problem. That is, Class D multiply inherits from Class B and Class C, and Class B and Class C each inherit from Class A. Thus when an object of type D is created, in turn instances of B and C are created, and unfortunately both will try to make their own instance of A. If two instances of A were allowed, attempts to refer to member variables or member functions of A would be ambiguous. To solve the problem, we should specify that B virtually inherits from A and C virtually inherits from A. Furthermore, when we construct an instance of D, in addition to specifying how to call constructors for B and C, we also explicitly specify the constructor for A.
|
||||
|
||||

|
||||
|
||||
<!--
|
||||
```cpp
|
||||
class A {
|
||||
public:
|
||||
A() {}
|
||||
};
|
||||
class B : virtual public A {
|
||||
public:
|
||||
B() : A() {}
|
||||
};
|
||||
class C : virtual public A {
|
||||
public:
|
||||
C() : A() {}
|
||||
};
|
||||
class D : public B, public C {
|
||||
public:
|
||||
D() : A(), B(), C() {}
|
||||
};
|
||||
```
|
||||
-->
|
||||
|
||||
Note how in the single inheritance example below, G only explicitly calls a constructor for F.
|
||||
|
||||

|
||||
|
||||
<!--
|
||||
```cpp
|
||||
class E {
|
||||
public:
|
||||
E() {}
|
||||
};
|
||||
class F : public E {
|
||||
public:
|
||||
F() : E() {}
|
||||
};
|
||||
class G : public F {
|
||||
public:
|
||||
G() : F() {}
|
||||
};
|
||||
```
|
||||
-->
|
||||
|
||||
Label the virtual inheritance paths in your diagram. Hint: 6 of the inheritance arrows will be labeled virtual.
|
||||
|
||||
**To complete this checkpoint**: Present your class inheritance diagram and your shape sketches to one of the TAs.
|
||||
|
||||
## Checkpoint 2:
|
||||
|
||||
*estimate: 20-40 minutes*
|
||||
|
||||
To start the implementation, we’ll focus on just 7 of those shapes: Polygon, Triangle, Quadrilateral, Isosceles Triangle, Equilateral Triangle, Rectangle, and Square. This subset will allow us to initially ignore the multiple inheritance diamond property and the need for virtual inheritance it causes.
|
||||
|
||||
Download the code and initial example: [simple_main.cpp](cp2/simple_main.cpp), [utilities.h](cp2/utilities.h), [simple.txt](cp2/simple.txt), [output_simple.txt](cp2/output_simple.txt).
|
||||
The program expects 2 command line arguments, the names of the input and output files. Each line of the input file begins with a string name followed by 3 or more 2D coordinate vertices. The output categorizes each shape into one or more classes, and into groups with equal angles, equal edges, and/or a right angle. The provided code includes code to call the constructors of the different classes, generally ordered from most specific/constrained to least specific. For example, the program will try to create a Square with the data first, and only if that constructor fails (throws an exception) then it will try to create a Rectangle. We also include a utilities.h file with a number of simple geometric operations: e.g., calculate the distance between two points, calculate the angle between two edges, and compare two distances or two angles and judge if they are sufficiently close to be called “equal”. Remember that you usually don’t want to check if two floating point numbers are equal; instead, check if the difference is below an appropriate tolerance.
|
||||
|
||||
Create a polygons.h file (and optionally a polygons.cpp file). Create the 7 classes for these shapes deriving classes from the other classes as appropriate. In each constructor write code to check whether the vertices passed in meet the requirements for that shape. Throw an exception if you find a problem. Note: Just throw a value of type integer. The value thrown is unimportant in this program – it will be ignored.
|
||||
|
||||
**To complete this checkpoint**: Compile, run, and debug your program. Study the output and confirm that your program is correctly labeling the shapes in simple.txt.
|
||||
|
||||
## Checkpoint 3:
|
||||
|
||||
*estimate: 20-30 minutes*
|
||||
|
||||
Now tackle the problem of multiple inheritance and the diamond property. Focus on either the [triangles](cp3/triangles.txt) or the [quadrilaterals](cp3/quads.txt) (more challenging). Here is a revised [main.cpp](cp3/main.cpp) for all shapes (comment out the portions you are not implementing).
|
||||
|
||||
In organizing your code for this lab, try to avoid unnecessarily duplicating code. For example, don’t implement the HasARightAngle function in every class. Also, you don’t need to check if the Rectangle has 4 vertices (the constructor for its base class will do that). Instead, allow the derived class to rely on the implementation of that function in its parent class. Similarly, don’t recalculate measurement data if you can deduce information from properties of that shape. For example, when a Rectangle is asked if it HasARightAngle, no calculation is necessary — the answer is guaranteed to be true.
|
||||
|
||||
**To complete this checkpoint**: Be sure to ask your TAs for help if you get stuck. Near the end of lab period show a TA your progress.
|
||||
31
labs/exceptions_inheritance/cp1/input.txt
Normal file
31
labs/exceptions_inheritance/cp1/input.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
black (1,0) (0.5,0.866) (1,1.732) (3,1.732) (3.5,0.866) (3,0)
|
||||
blue (0,0) (0,1) (1,1) (1,0)
|
||||
brown (2,0) (0,1) (3,3)
|
||||
cyan (0,0) (0,2) (1,0)
|
||||
green (0,0) (0,1) (1,0)
|
||||
grey (1,0) (1.5,0.866) (1,1.732) (2,1.732) (2.5,0.866) (2,0)
|
||||
magenta (0,0) (1,1) (4,1) (3,0)
|
||||
olive (0,0) (1,2) (6,3) (4,0)
|
||||
orange (0,0) (0,3) (2,3) (2,0)
|
||||
pink (1,0) (0,2) (4,2) (3,0)
|
||||
purple (0,0) (0,2) (3,1)
|
||||
red (0,0) (0.5,0.866) (1,0)
|
||||
teal (1,0) (0,3) (1,4) (2,3)
|
||||
white (2,0) (0,1) (3,4) (6,1) (4,0)
|
||||
yellow (0,1) (2,2) (4,1) (2,0)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
8
labs/exceptions_inheritance/cp2/output_simple.txt
Normal file
8
labs/exceptions_inheritance/cp2/output_simple.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
7 Polygon(s): blue brown grey olive orange purple red
|
||||
3 Triangle(s): brown purple red
|
||||
2 IsoscelesTriangle(s): purple red
|
||||
1 EquilateralTriangle(s): red
|
||||
3 Quadrilateral(s): blue olive orange
|
||||
2 Rectangle(s): blue orange
|
||||
1 Square(s): blue
|
||||
3 Shape(s) with all equal sides: blue grey red
|
||||
7
labs/exceptions_inheritance/cp2/simple.txt
Normal file
7
labs/exceptions_inheritance/cp2/simple.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
blue (0,0) (0,1) (1,1) (1,0)
|
||||
brown (2,0) (0,1) (3,3)
|
||||
grey (1,0) (1.5,0.866) (1,1.732) (2,1.732) (2.5,0.866) (2,0)
|
||||
olive (0,0) (1,2) (6,3) (4,0)
|
||||
orange (0,0) (0,3) (2,3) (2,0)
|
||||
purple (0,0) (0,2) (3,1)
|
||||
red (0,0) (0.5,0.866) (1,0)
|
||||
168
labs/exceptions_inheritance/cp2/simple_main.cpp
Normal file
168
labs/exceptions_inheritance/cp2/simple_main.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#include "polygons.h"
|
||||
|
||||
// helper function prototypes
|
||||
Polygon* CreatePolygon(const std::string &name, const std::vector<Point> &points);
|
||||
void OutputStats(const std::vector<Polygon*> &polygons, std::ofstream &ostr);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
// command line arguments & opening files
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " input.txt output.txt" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::ifstream istr(argv[1]);
|
||||
if (!istr) {
|
||||
std::cerr << "ERROR: could not open " << argv[1] << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::ofstream ostr(argv[2]);
|
||||
if (!ostr) {
|
||||
std::cerr << "ERROR: could not open " << argv[2] << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// the master container of polygons
|
||||
std::vector<Polygon*> polygons;
|
||||
|
||||
// read the input file one line at a time
|
||||
std::string line;
|
||||
while (getline(istr,line)) {
|
||||
std::stringstream ss(line);
|
||||
std::string name, token;
|
||||
if (!(ss >> name)) continue;
|
||||
std::vector<Point> points;
|
||||
while (ss >> token) {
|
||||
std::stringstream ss2(token);
|
||||
char c;
|
||||
double x,y;
|
||||
ss2 >> c;
|
||||
assert (c == '(');
|
||||
ss2 >> x;
|
||||
ss2 >> c;
|
||||
assert (c == ',');
|
||||
ss2 >> y;
|
||||
ss2 >> c;
|
||||
assert (c == ')');
|
||||
points.push_back(Point(x,y));
|
||||
}
|
||||
assert (points.size() >= 3);
|
||||
Polygon* p = CreatePolygon(name,points);
|
||||
// add the new polygon to the master container
|
||||
polygons.push_back(p);
|
||||
}
|
||||
|
||||
// write out the statistics
|
||||
OutputStats(polygons,ostr);
|
||||
|
||||
// delete the dynamically allocated polygons
|
||||
for (int i = 0; i < polygons.size(); i++) {
|
||||
delete polygons[i];
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// This function determines the most specific type of shape that can
|
||||
// be created from the sequence of points. It does this by process of
|
||||
// elimination. Note that the order in which it attempts to create
|
||||
// the shapes is important.
|
||||
|
||||
Polygon* CreatePolygon(const std::string &name, const std::vector<Point> &points) {
|
||||
Polygon *answer = NULL;
|
||||
try{
|
||||
answer = new EquilateralTriangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new IsoscelesTriangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Triangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Square(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Rectangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Quadrilateral(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
answer= new Polygon(name,points);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert (answer != NULL);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// This function prints the output. C++ macros are used to abbreviate
|
||||
// some repetitive code. The function call-like macros are actually
|
||||
// replaced using substitution by the preprocessor before the code is
|
||||
// given to the compiler. (You are not required to understand the
|
||||
// details of the macros. You do not need to edit this code.)
|
||||
|
||||
void OutputStats(const std::vector<Polygon*> &polygons, std::ofstream &ostr) {
|
||||
|
||||
// define and initialize variables
|
||||
# define InitializeCount(type) std::vector<std::string> all_##type
|
||||
InitializeCount(Polygon);
|
||||
InitializeCount(Triangle);
|
||||
InitializeCount(IsoscelesTriangle);
|
||||
InitializeCount(EquilateralTriangle);
|
||||
InitializeCount(Quadrilateral);
|
||||
InitializeCount(Rectangle);
|
||||
InitializeCount(Square);
|
||||
std::vector<std::string> equal_sides;
|
||||
|
||||
// count & record the names of shapes in each category
|
||||
for (std::vector<Polygon*>::const_iterator i = polygons.begin(); i!=polygons.end(); ++i) {
|
||||
# define IncrementCount(type) if (dynamic_cast<type*> (*i)) all_##type.push_back((*i)->getName())
|
||||
IncrementCount(Polygon);
|
||||
IncrementCount(Triangle);
|
||||
IncrementCount(IsoscelesTriangle);
|
||||
IncrementCount(EquilateralTriangle);
|
||||
IncrementCount(Quadrilateral);
|
||||
IncrementCount(Rectangle);
|
||||
IncrementCount(Square);
|
||||
if ((*i)->HasAllEqualSides()) equal_sides.push_back((*i)->getName());
|
||||
}
|
||||
|
||||
|
||||
// output data for each category, sorted alphabetically by the shape's name
|
||||
# define PrintVector(vecname) std::sort((vecname).begin(),(vecname).end()); \
|
||||
for (unsigned int j = 0; j < (vecname).size(); j++) { ostr << " " << (vecname)[j]; } ostr << std::endl
|
||||
# define PrintCount(type) do { ostr << all_##type.size() << " " #type"(s): "; PrintVector(all_##type); } while (0)
|
||||
PrintCount(Polygon);
|
||||
PrintCount(Triangle);
|
||||
PrintCount(IsoscelesTriangle);
|
||||
PrintCount(EquilateralTriangle);
|
||||
PrintCount(Quadrilateral);
|
||||
PrintCount(Rectangle);
|
||||
PrintCount(Square);
|
||||
ostr << equal_sides.size() << " Shape(s) with all equal sides: ";
|
||||
PrintVector(equal_sides);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
120
labs/exceptions_inheritance/cp2/utilities.h
Normal file
120
labs/exceptions_inheritance/cp2/utilities.h
Normal file
@@ -0,0 +1,120 @@
|
||||
// =========================================
|
||||
//
|
||||
// IMPORTANT NOTE: DO NOT EDIT THIS FILE
|
||||
//
|
||||
// =========================================
|
||||
|
||||
#ifndef _UTILITIES_H_
|
||||
#define _UTILITIES_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
// epsilon values used in comparing the edge lengths & angles between
|
||||
// edges note that these values are dependent on the precision of
|
||||
// the coordinates and the overall scale of the objects
|
||||
#define DISTANCE_EPSILON 0.0001
|
||||
#define ANGLE_EPSILON 0.1
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// Stores a 2D coordinate
|
||||
class Point {
|
||||
public:
|
||||
Point(double _x, double _y) : x(_x),y(_y) {}
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
// Stores a 2D vector, constructed from 2 Points
|
||||
class Vector {
|
||||
public:
|
||||
Vector(const Point &a, const Point &b) { dx = b.x-a.x; dy = b.y-a.y; }
|
||||
double Length() const { return sqrt(dx*dx+dy*dy); }
|
||||
void Normalize() {
|
||||
// make this a unit vector (length = 1)
|
||||
double length = Length();
|
||||
if (length < DISTANCE_EPSILON) throw std::string("LENGTH = 0");
|
||||
assert (length > DISTANCE_EPSILON);
|
||||
dx /= length;
|
||||
dy /= length;
|
||||
}
|
||||
// representation
|
||||
double dx;
|
||||
double dy;
|
||||
};
|
||||
|
||||
|
||||
inline std::ostream& operator<< (std::ostream &ostr, const Vector &v) {
|
||||
ostr << "<" << v.dx << "," << v.dy << ">";
|
||||
return ostr;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// calculate the length of an edge, the distance between 2 points
|
||||
inline double DistanceBetween(const Point &a, const Point &b) {
|
||||
Vector v(a,b);
|
||||
return v.Length();
|
||||
}
|
||||
|
||||
// Calculate the angle at vertex b in degrees, that is, the angle
|
||||
// between edges ab and bc. This will return a positive number
|
||||
// measured as the clockwise rotation in the xy plane from point c to
|
||||
// point a (rotating around point b).
|
||||
inline double Angle(const Point &a, const Point &b, const Point &c) {
|
||||
// make unit vector along each of the edges
|
||||
Vector ba(b,a); ba.Normalize();
|
||||
Vector bc(b,c); bc.Normalize();
|
||||
// calculate the angle in radians
|
||||
double dot_product = ba.dx * bc.dx + ba.dy * bc.dy;
|
||||
|
||||
if (dot_product < -1 || dot_product > 1) throw std::string("DOT PRODUCT RANGE");
|
||||
|
||||
assert (dot_product >= -1 && dot_product <= 1);
|
||||
float perpDot = ba.dx * bc.dy - ba.dy * bc.dx;
|
||||
// using atan2 to ensure than we get a signed answer.
|
||||
double angle_in_radians = atan2(perpDot, dot_product);
|
||||
// convert to degrees
|
||||
double answer = angle_in_radians * 180.0 / M_PI;
|
||||
if (answer < 0) {
|
||||
answer += 360;
|
||||
}
|
||||
assert (answer >= 0 && answer <= 360);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// returns true if these two vectors are parallel
|
||||
inline bool Parallel(const Vector &a, const Vector &b) {
|
||||
Vector a2 = a; a2.Normalize();
|
||||
Vector b2 = b; b2.Normalize();
|
||||
double dot_product = a2.dx * b2.dx + a2.dy * b2.dy;
|
||||
// parallel vectors have dot product == 1
|
||||
if (fabs(dot_product) > 1-DISTANCE_EPSILON) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// simple functions for angles & sides
|
||||
|
||||
inline bool EqualSides(double a, double b) {
|
||||
return (fabs(a-b) < DISTANCE_EPSILON);
|
||||
}
|
||||
|
||||
inline bool EqualAngles(double a, double b) {
|
||||
assert (a >= 0.0 && a < 360.0);
|
||||
assert (b >= 0.0 && b < 360.0);
|
||||
return (fabs(a-b) < ANGLE_EPSILON);
|
||||
}
|
||||
|
||||
inline bool RightAngle(double a) {
|
||||
assert (a >= 0.0 && a < 360.0);
|
||||
return (fabs(a-90.0) < ANGLE_EPSILON);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
#endif
|
||||
223
labs/exceptions_inheritance/cp3/main.cpp
Normal file
223
labs/exceptions_inheritance/cp3/main.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
#include "polygons.h"
|
||||
|
||||
// helper function prototypes
|
||||
Polygon* CreatePolygon(const std::string &name, const std::vector<Point> &points);
|
||||
void OutputStats(const std::vector<Polygon*> &polygons, std::ofstream &ostr);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
// command line arguments & opening files
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " input.txt output.txt" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::ifstream istr(argv[1]);
|
||||
if (!istr) {
|
||||
std::cerr << "ERROR: could not open " << argv[1] << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
std::ofstream ostr(argv[2]);
|
||||
if (!ostr) {
|
||||
std::cerr << "ERROR: could not open " << argv[2] << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// the master container of polygons
|
||||
std::vector<Polygon*> polygons;
|
||||
|
||||
// read the input file one line at a time
|
||||
std::string line;
|
||||
while (getline(istr,line)) {
|
||||
std::stringstream ss(line);
|
||||
std::string name, token;
|
||||
if (!(ss >> name)) continue;
|
||||
std::vector<Point> points;
|
||||
while (ss >> token) {
|
||||
std::stringstream ss2(token);
|
||||
char c;
|
||||
double x,y;
|
||||
ss2 >> c;
|
||||
assert (c == '(');
|
||||
ss2 >> x;
|
||||
ss2 >> c;
|
||||
assert (c == ',');
|
||||
ss2 >> y;
|
||||
ss2 >> c;
|
||||
assert (c == ')');
|
||||
points.push_back(Point(x,y));
|
||||
}
|
||||
assert (points.size() >= 3);
|
||||
Polygon* p = CreatePolygon(name,points);
|
||||
// add the new polygon to the master container
|
||||
polygons.push_back(p);
|
||||
}
|
||||
|
||||
// write out the statistics
|
||||
OutputStats(polygons,ostr);
|
||||
|
||||
// delete the dynamically allocated polygons
|
||||
for (int i = 0; i < polygons.size(); i++) {
|
||||
delete polygons[i];
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// This function determines the most specific type of shape that can
|
||||
// be created from the sequence of points. It does this by process of
|
||||
// elimination. Note that the order in which it attempts to create
|
||||
// the shapes is important.
|
||||
|
||||
Polygon* CreatePolygon(const std::string &name, const std::vector<Point> &points) {
|
||||
Polygon *answer = NULL;
|
||||
try{
|
||||
answer = new EquilateralTriangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new IsoscelesRightTriangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new RightTriangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new IsoscelesTriangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Triangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Square(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Rectangle(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Rhombus(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Parallelogram(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Kite(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Trapezoid(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
try {
|
||||
answer= new Quadrilateral(name,points);
|
||||
}
|
||||
catch (int) {
|
||||
answer= new Polygon(name,points);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert (answer != NULL);
|
||||
return answer;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
// This function prints the output. C++ macros are used to abbreviate
|
||||
// some repetitive code. The function call-like macros are actually
|
||||
// replaced using substitution by the preprocessor before the code is
|
||||
// given to the compiler. (You are not required to understand the
|
||||
// details of the macros. You do not need to edit this code.)
|
||||
|
||||
void OutputStats(const std::vector<Polygon*> &polygons, std::ofstream &ostr) {
|
||||
|
||||
// define and initialize variables
|
||||
# define InitializeCount(type) std::vector<std::string> all_##type
|
||||
InitializeCount(Polygon);
|
||||
InitializeCount(Triangle);
|
||||
InitializeCount(IsoscelesTriangle);
|
||||
InitializeCount(RightTriangle);
|
||||
InitializeCount(IsoscelesRightTriangle);
|
||||
InitializeCount(EquilateralTriangle);
|
||||
InitializeCount(Quadrilateral);
|
||||
InitializeCount(Trapezoid);
|
||||
InitializeCount(Kite);
|
||||
InitializeCount(Parallelogram);
|
||||
InitializeCount(Rhombus);
|
||||
InitializeCount(Rectangle);
|
||||
InitializeCount(Square);
|
||||
std::vector<std::string> equal_sides;
|
||||
std::vector<std::string> equal_angles;
|
||||
std::vector<std::string> right_angle;
|
||||
|
||||
// count & record the names of shapes in each category
|
||||
for (std::vector<Polygon*>::const_iterator i = polygons.begin(); i!=polygons.end(); ++i) {
|
||||
# define IncrementCount(type) if (dynamic_cast<type*> (*i)) all_##type.push_back((*i)->getName())
|
||||
IncrementCount(Polygon);
|
||||
IncrementCount(Triangle);
|
||||
IncrementCount(IsoscelesTriangle);
|
||||
IncrementCount(RightTriangle);
|
||||
IncrementCount(IsoscelesRightTriangle);
|
||||
IncrementCount(EquilateralTriangle);
|
||||
IncrementCount(Quadrilateral);
|
||||
IncrementCount(Trapezoid);
|
||||
IncrementCount(Kite);
|
||||
IncrementCount(Parallelogram);
|
||||
IncrementCount(Rhombus);
|
||||
IncrementCount(Rectangle);
|
||||
IncrementCount(Square);
|
||||
if ((*i)->HasAllEqualSides()) equal_sides.push_back((*i)->getName());
|
||||
if ((*i)->HasAllEqualAngles()) equal_angles.push_back((*i)->getName());
|
||||
if ((*i)->HasARightAngle()) right_angle.push_back((*i)->getName());
|
||||
}
|
||||
|
||||
// output data for each category, sorted alphabetically by the shape's name
|
||||
# define PrintVector(vecname) std::sort((vecname).begin(),(vecname).end()); \
|
||||
for (unsigned int j = 0; j < (vecname).size(); j++) { ostr << " " << (vecname)[j]; } ostr << std::endl
|
||||
# define PrintCount(type) do { ostr << all_##type.size() << " " #type"(s): "; PrintVector(all_##type); } while (0)
|
||||
PrintCount(Polygon);
|
||||
PrintCount(Triangle);
|
||||
PrintCount(IsoscelesTriangle);
|
||||
PrintCount(RightTriangle);
|
||||
PrintCount(IsoscelesRightTriangle);
|
||||
PrintCount(EquilateralTriangle);
|
||||
PrintCount(Quadrilateral);
|
||||
PrintCount(Trapezoid);
|
||||
PrintCount(Kite);
|
||||
PrintCount(Parallelogram);
|
||||
PrintCount(Rhombus);
|
||||
PrintCount(Rectangle);
|
||||
PrintCount(Square);
|
||||
ostr << equal_sides.size() << " Shape(s) with all equal sides: ";
|
||||
PrintVector(equal_sides);
|
||||
ostr << equal_angles.size() << " Shape(s) with all equal angles: ";
|
||||
PrintVector(equal_angles);
|
||||
ostr << right_angle.size() << " Shape(s) with a right angle: ";
|
||||
PrintVector(right_angle);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
9
labs/exceptions_inheritance/cp3/quads.txt
Normal file
9
labs/exceptions_inheritance/cp3/quads.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
blue (0,0) (0,1) (1,1) (1,0)
|
||||
orange (0,0) (0,3) (2,3) (2,0)
|
||||
yellow (0,1) (2,2) (4,1) (2,0)
|
||||
magenta (0,0) (1,1) (4,1) (3,0)
|
||||
pink (1,0) (0,2) (4,2) (3,0)
|
||||
teal (1,0) (0,3) (1,4) (2,3)
|
||||
olive (0,0) (1,2) (6,3) (4,0)
|
||||
|
||||
|
||||
9
labs/exceptions_inheritance/cp3/triangles.txt
Normal file
9
labs/exceptions_inheritance/cp3/triangles.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
red (0,0) (0.5,0.866) (1,0)
|
||||
green (0,0) (0,1) (1,0)
|
||||
cyan (0,0) (0,2) (1,0)
|
||||
purple (0,0) (0,2) (3,1)
|
||||
brown (2,0) (0,1) (3,3)
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
labs/exceptions_inheritance/images/multiple_inheritance.png
Normal file
BIN
labs/exceptions_inheritance/images/multiple_inheritance.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
labs/exceptions_inheritance/images/single_inheritance.png
Normal file
BIN
labs/exceptions_inheritance/images/single_inheritance.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
91
labs/hash_tables/README.md
Normal file
91
labs/hash_tables/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Lab 12 — Hash Tables
|
||||
|
||||
<!--In this lab, you will first experiment with our hash table implementation of a set. The key differences between the ds_set class (based on a binary search tree) and the ds_hashset class (based on a hash table, of course), are the performance of insert/find/erase: O(log n) vs. O(1), and the order that the elements are traversed using iterators: the set was in order, while the hashset is in no apparent order.-->
|
||||
|
||||
In this lab, you will practice using std::unordered_set, std::unordered_map, and construct your own separate-chaining-based hash table.
|
||||
|
||||
<!--Provided code for checkpoint 1 and checkpoint 2: [ds_hashset.h](ds_hashset.h) and [test_ds_hashset.cpp](test_ds_hashset.cpp).-->
|
||||
|
||||
## Checkpoint 1: Using std::unordered_set and std::unordered_map
|
||||
|
||||
*estimate: 15-30 minutes*
|
||||
|
||||
Complete the *isGood* function in this [program](good_number.cpp). This function determines if a number n is good or not. You can assume 1<=n<50000. You must write two versions of the function, one version uses std::unordered_set, the other version uses std::unordered_map.
|
||||
|
||||
A good number is a number defined by the following process:
|
||||
|
||||
- Starting with any positive integer, replace the number by the sum of the squares of its digits.
|
||||
- Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
|
||||
- Those numbers for which this process ends in 1 are good.
|
||||
|
||||
Return true if n is a good number, and false if not. Here are some examples:
|
||||
|
||||
```console
|
||||
Example 1:
|
||||
|
||||
Input: n = 19
|
||||
Output: true
|
||||
|
||||
Explanation:
|
||||
1^2 + 9^2 = 82
|
||||
8^2 + 2^2 = 68
|
||||
6^2 + 8^2 = 100
|
||||
1^2 + 0^2 + 0^2 = 1
|
||||
```
|
||||
|
||||
```console
|
||||
Example 2:
|
||||
|
||||
Input: n = 2
|
||||
Output: false
|
||||
```
|
||||
|
||||
<!--For the first part of this checkpoint, implement and test the *insert* function for the hashset. The *insert* function must first determine in which bin the new element belongs (using the hash function), and then insert the element into that bin but only if it isn’t there already. The *insert* function returns a pair containing an iterator pointing at the element, and a bool indicating whether it was successfully inserted (true) or already there (false).
|
||||
|
||||
For the second part of this checkpoint, experiment with the hash function. In the provided code we include the implementation of a good hash function for strings. Are there any collisions for the small example? Now write some alternative hash functions. First, create a trivial hash function that is guaranteed to have many, many collisions. Then, create a hash function that is not terrible, but will unfortunately always place anagrams (words with the same letters, but rearranged) in the same bin. Test your alternate functions and be prepared to show the results to your TA.
|
||||
|
||||
**To complete this checkpoint**: Show a TA your debugged implementation of *insert* and your experimentation with alternative hash functions.-->
|
||||
|
||||
**To complete this checkpoint**: Show a TA the two versions of your program and the test results.
|
||||
|
||||
## Checkpoint 2: Separate Chaining Hash Table
|
||||
|
||||
*estimate: 30-40 minutes*
|
||||
|
||||
<!--Next, implement and test the *begin* function, which initializes the iteration through a hashset. Confirm that the elements in the set are visited in the same order they appear with the *print* function (which we have implemented for debugging purposes only).
|
||||
|
||||
Finally, implement and test the *resize* function. This function is automatically called from the *insert* function when the set gets “too full”. This function should make a new top level vector structure of the requested size and copy all the data from the old structure to the new structure. Note that the elements will likely be shuffled around from the old structure to the new structure.-->
|
||||
|
||||
Complete the *isGood* function using separate chaining. Do not use any of these: std::unordered_map, std::unordered_set, std::map, std::set.
|
||||
|
||||
**To complete this checkpoint**: Show a TA these additions and the test output.
|
||||
|
||||
<!--## Checkpoint 3,4,5,6: Separate Chaining Hash Table (Yes, it's Checkpoint 3,4,5,6, as there are 3 extra credits for making this program work.)-->
|
||||
## Checkpoint 3
|
||||
|
||||
*estimate: 30-40 minutes*
|
||||
|
||||
Form a team of two members. Complete the *longestConsecutive* function in this [program](test_longest_consecutive_sequence.cpp). You must use a separate-chaining-based hash table. Do not use any of these: std::unordered_map, std::unordered_set, std::map, std::set.
|
||||
|
||||
The *longestConsecutive* function takes an unsorted std::vector of integers, and returns the length of the longest consecutive elements sequence.
|
||||
|
||||
```console
|
||||
Example 1:
|
||||
|
||||
Input: nums = [100,4,200,1,3,2]
|
||||
Output: 4
|
||||
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
|
||||
```
|
||||
|
||||
```console
|
||||
Example 2:
|
||||
|
||||
Input: nums = [0,3,7,2,5,8,4,6,0,1]
|
||||
Output: 9
|
||||
Explanation: The longest consecutive elements sequence is [0, 1, 2, 3, 4, 5, 6, 7, 8]. Therefore its length is 9.
|
||||
```
|
||||
|
||||
**To complete this checkpoint**: Show a TA your program and the test results.
|
||||
<!--**To complete checkpoint (3,4,5,6)**: Show a TA your program and the test results.-->
|
||||
|
||||
<!--**Note**: checkpoint (3,4,5,6) is very challenging, if you can't get it work during your lab period, make sure you understand it completely after the lab period, because one of the four problems on Test 3, will be about separate-chaining-based hash tables. It's 100% certain.-->
|
||||
23
labs/hash_tables/good_number.cpp
Normal file
23
labs/hash_tables/good_number.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <iostream>
|
||||
|
||||
bool isGood(int n) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Test cases
|
||||
// 2, 4, 5, 6, 17, 18, 20 are not good numbers.
|
||||
// 1, 7, 10, 13, 19, 23, 28, 68 are good numbers.
|
||||
|
||||
int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68};
|
||||
|
||||
for (int n : testCases) {
|
||||
if (isGood(n)) {
|
||||
std::cout << n << " is a good number." << std::endl;
|
||||
} else {
|
||||
std::cout << n << " is not a good number." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
23
labs/hash_tables/test_longest_consecutive_sequence.cpp
Normal file
23
labs/hash_tables/test_longest_consecutive_sequence.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
int longestConsecutive(std::vector<int>& nums) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
//std::vector<int> nums = {100, 4, 200, 1, 3, 2};
|
||||
std::vector<int> nums = {100, 4, 200, 1, 3, 2, 2, 2, 2, 3};
|
||||
//std::vector<int> nums = {100, 4, 200, 1, 3, 2, 5, 6};
|
||||
//std::vector<int> nums = {0,3,7,2,5,8,4,6,0,1};
|
||||
//std::vector<int> nums = {100, 4, 200, 201, 202, 203, 205, 204, 1, 3, 2};
|
||||
//std::vector<int> nums = {-3,0,1,2,3,-2,-1,-5};
|
||||
int size = nums.size();
|
||||
std::cout<< "for vector {";
|
||||
for(int i=0;i<size-1;i++){
|
||||
std::cout<< nums[i] << ",";
|
||||
}
|
||||
std::cout<< nums[size-1] << "}" <<std::endl;
|
||||
int length = longestConsecutive(nums);
|
||||
std::cout << "The length of the longest consecutive sequence is: " << length << std::endl;
|
||||
return 0;
|
||||
}
|
||||
72
labs/smart_memory/README.md
Normal file
72
labs/smart_memory/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Lab 14 — Garbage Collection & Smart Pointers
|
||||
|
||||
## Checkpoint 0
|
||||
|
||||
If you haven’t done so already, please complete your course evaluation for Data Structures (your honest & anonymous feedback is very important!). Have the webpage receipt saying “completed” open in your browser to receive credit for Checkpoint 0.
|
||||
|
||||
## Checkpoint 1:
|
||||
|
||||
*estimate: 20-40 minutes*
|
||||
|
||||
For the first checkpoint, download, compile, and run these files: [stop_and_copy.h](stop_and_copy.h), [stop_and_copy.cpp](stop_and_copy.cpp), [main_stop_and_copy.cpp](main_stop_and_copy.cpp)
|
||||
|
||||
In Lecture, we stepped through the Stop and Copy garbage collection algorithm on a small example.
|
||||
Examine the output of the program to see a computer simulation of this same example. Verify that the
|
||||
program behaves as we predicted in lecture.
|
||||
|
||||
Continuing with the same example, 3 more nodes have been added and the garbage collector must be run
|
||||
again. Draw a “box and pointer” diagram (the more usual human-friendly version of the interconnected
|
||||
node structure with few or no crossing pointer arrows) of the memory accessible from the root pointer after
|
||||
these 3 nodes are added and work through the Stop and Copy algorithm for this example on paper. When
|
||||
you are finished, uncomment the simulation and check your work.
|
||||
|
||||
**To complete this checkpoint**: Show (and explain to) one of the TAs your box and pointer diagram.
|
||||
|
||||
## Checkpoint 2:
|
||||
|
||||
*estimate: 20-40 minutes*
|
||||
|
||||
The theme for this checkpoint are the helium filled balloons for the Macy’s Thanksgiving Day parade. These
|
||||
balloons are held in place by one or more ropes held by people on the ground. Alternately, balloons may
|
||||
be connected to other balloons that are held by people! People can swap which balloon they are holding
|
||||
on to, but if everyone holding on to the ropes for a particular balloon lets go, we will have a big problem!
|
||||
Download, compile, and run these files: [ds_smart_pointers.h](ds_smart_pointers.h) and [main_smart_pointers.cpp](main_smart_pointers.cpp).
|
||||
Use Dr. Memory or Valgrind to inspect the initial code for memory errors and/or memory leaks.
|
||||
|
||||
Carefully examine the example allocations in the main function of the provided code. Draw simple pictures
|
||||
to help keep track of which objects have been allocated with new, and which variables currently point to
|
||||
each dynamically allocated object.
|
||||
|
||||
To fix the memory leaks in this program, you will need to add explicit deallocation for the non-smart pointer
|
||||
examples (marked CHECKPOINT 2A and 2B). For comparison, the code includes simple examples of smart
|
||||
pointers for these objects as well! When we know that just one person will hold on to a Balloon at a time
|
||||
(one owner) we can use a dsAutoPtr (see also the STL auto_ptr or STL unique_ptr, depending on your
|
||||
version of g++/clang/STL). When multiple people might hold ropes to the same Balloon, we should use a
|
||||
dsSharedPtr (see also STL shared_ptr or Boost shared_ptr). A shared pointer uses reference counting!
|
||||
When the last person disconnects from a Balloon using a shared pointer, the Balloon is automatically deleted.
|
||||
|
||||
Re-compile & re-run with the memory debugger to confirm you have fixed the simple leaks. For the final
|
||||
piece of this checkpoint (marked CHECKPOINT 2C), you must also re-rewrite the interconnected balloon
|
||||
example to use shared pointers. You will need to modify the Balloon class to use dsSharedPointer as well.
|
||||
|
||||
**To complete this checkpoint**: Explain to your TA the code you needed to add and/or modify to correct
|
||||
the memory leaks in the provided code. Show your TA the result of the memory debugger on your finished
|
||||
implementation.
|
||||
|
||||
## Checkpoint 3:
|
||||
|
||||
*estimate: 20-40 minutes*
|
||||
|
||||
For the last checkpoint, let’s consider cyclic balloon structures. A simple example is included at the bottom
|
||||
of main_smart_pointer.cpp. Draw a diagram of this structure on paper. The provided code has memory
|
||||
leaks for this example. We could try to re-write this example to use the shared smart pointer. However, a
|
||||
reference counting smart pointer will still have a problem on this cyclic example. Why?
|
||||
|
||||
Instead, let’s write a helper function to explicitly deallocate a general cyclic structure of Balloon objects. (We
|
||||
will not use smart pointers for this checkpoint). You should switch back to the original Balloon class (if you
|
||||
modified it for Checkpoint 2). You will first need to collect all nodes that are reachable from the provided
|
||||
argument. Make sure that you do not enter an infinite loop when tracing through the structure! You may
|
||||
find std::set helpful. Once you have identified all of the nodes that are accessible from the input argument,
|
||||
you can call delete on each Node. Write additional test cases to confirm that your code is debugged.
|
||||
|
||||
**To complete this checkpoint**: Show the TA cyclic memory diagram, your debugged implementation, and the memory debugger output to confirm that your code has no memory errors or leaks.
|
||||
77
labs/smart_memory/ds_smart_pointers.h
Normal file
77
labs/smart_memory/ds_smart_pointers.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <cstdlib>
|
||||
|
||||
// ==============================================================================
|
||||
// basic auto pointer implementation
|
||||
// see also http://ootips.org/yonat/4dev/smart-pointers.html
|
||||
|
||||
template <class T>
|
||||
class dsAutoPtr {
|
||||
public:
|
||||
explicit dsAutoPtr(T* p = NULL) : ptr(p) {} /* prevents cast/conversion */
|
||||
~dsAutoPtr() { delete ptr; }
|
||||
T& operator*() { return *ptr; }
|
||||
T* operator->() { return ptr; }
|
||||
private:
|
||||
T* ptr;
|
||||
};
|
||||
|
||||
|
||||
// ==============================================================================
|
||||
// basic reference counting pointer borrowed from:
|
||||
// http://www.codeproject.com/Articles/15351/Implementing-a-simple-smart-pointer-in-c
|
||||
|
||||
class ReferenceCount {
|
||||
public:
|
||||
ReferenceCount() { count = 0; }
|
||||
void addReference() { count++; }
|
||||
int releaseReference() { return --count; }
|
||||
private:
|
||||
int count;
|
||||
};
|
||||
|
||||
|
||||
template <class T>
|
||||
class dsSharedPtr {
|
||||
public:
|
||||
dsSharedPtr(T* pValue = NULL) : pData(pValue) {
|
||||
// Create a new reference counter & increment the count
|
||||
reference = new ReferenceCount();
|
||||
reference->addReference();
|
||||
}
|
||||
dsSharedPtr(const dsSharedPtr<T>& sp) : pData(sp.pData), reference(sp.reference) {
|
||||
// use the same reference counter, increment the count
|
||||
reference->addReference();
|
||||
}
|
||||
dsSharedPtr<T>& operator= (const dsSharedPtr<T>& sp) {
|
||||
if (this != &sp) {
|
||||
// Decrement the old reference count
|
||||
// if reference become zero delete the old data
|
||||
if(reference->releaseReference() == 0) {
|
||||
delete pData;
|
||||
delete reference;
|
||||
}
|
||||
// Copy the data and reference pointer
|
||||
// and increment the reference count
|
||||
pData = sp.pData;
|
||||
reference = sp.reference;
|
||||
reference->addReference();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
// destructor
|
||||
~dsSharedPtr() {
|
||||
if (reference->releaseReference() == 0) {
|
||||
delete pData;
|
||||
delete reference;
|
||||
}
|
||||
}
|
||||
bool operator== (const dsSharedPtr<T>& sp) { return pData == sp.pData; }
|
||||
T& operator* () { return *pData; }
|
||||
T* operator-> () { return pData; }
|
||||
private:
|
||||
// REPRESENTATION
|
||||
T* pData;
|
||||
ReferenceCount* reference;
|
||||
};
|
||||
|
||||
// ==============================================================================
|
||||
208
labs/smart_memory/main_smart_pointers.cpp
Normal file
208
labs/smart_memory/main_smart_pointers.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
// simple homemade smart pointers
|
||||
#include "ds_smart_pointers.h"
|
||||
|
||||
|
||||
// ====================================================
|
||||
// BALLOON: a toy class with dynamically allocated memory
|
||||
// ====================================================
|
||||
|
||||
#define MAX_NUM_ROPES 10
|
||||
|
||||
class Balloon {
|
||||
public:
|
||||
// CONSTRUCTOR & DESTRUCTOR
|
||||
Balloon(const std::string& name_) : name(name_) {
|
||||
std::cout << "Balloon constructor " << name << std::endl;
|
||||
num_ropes = 0;
|
||||
ropes = new Balloon*[MAX_NUM_ROPES];
|
||||
}
|
||||
~Balloon() {
|
||||
std::cout << "Balloon destructor " << name << std::endl;
|
||||
// don't try to destroy attached balloons, just delete array
|
||||
delete [] ropes;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
const std::string& getName() const { return name; }
|
||||
int numRopes() const { return num_ropes; }
|
||||
const Balloon* getRope(int i) const { return ropes[i]; }
|
||||
|
||||
// print the balloons we are attached to
|
||||
void print() {
|
||||
std::cout << "Balloon " << name << " is connected to: ";
|
||||
for (int i = 0; i < num_ropes; i++) {
|
||||
std::cout << ropes[i]->name << " ";
|
||||
}
|
||||
if (num_ropes == 0) std::cout << "nothing";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// add a rope connecting this balloon to another
|
||||
void addRope(Balloon* b) {
|
||||
assert (num_ropes < MAX_NUM_ROPES);
|
||||
ropes[num_ropes] = b;
|
||||
num_ropes++;
|
||||
}
|
||||
// detach a rope connecting this balloon to another
|
||||
void removeRope(Balloon* b) {
|
||||
for (int i = 0; i < MAX_NUM_ROPES; i++) {
|
||||
if (ropes[i] == b) { ropes[i] = ropes[num_ropes-1]; num_ropes--; break; }
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
int num_ropes;
|
||||
// a dynamically allocated C-style array of ropes connecting to other Balloons
|
||||
Balloon** ropes;
|
||||
};
|
||||
|
||||
|
||||
// ====================================================
|
||||
// ====================================================
|
||||
|
||||
int main() {
|
||||
|
||||
std::cout << "start of main" << std::endl;
|
||||
|
||||
// ====================================================
|
||||
// SINGLE OWNER SMART POINTERS
|
||||
// ====================================================
|
||||
|
||||
// first, without smart pointers!
|
||||
Balloon* alice(new Balloon("Hello Kitty"));
|
||||
|
||||
// now, with our homemade single owner smart pointer
|
||||
dsAutoPtr<Balloon> bob(new Balloon("Spiderman"));
|
||||
|
||||
// both alice & bob work like regular pointers...
|
||||
alice->print();
|
||||
bob->print();
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CHECKPOINT 2A: INSERT NECESSARY EXPLICIT DEALLOCATION
|
||||
//
|
||||
|
||||
|
||||
|
||||
// ====================================================
|
||||
// SIMPLE SHARED POINTERS
|
||||
// ====================================================
|
||||
|
||||
// first, without smart pointers
|
||||
Balloon* cathy(new Balloon("Buzz Lightyear"));
|
||||
Balloon* daniel(cathy);
|
||||
Balloon* elaine(new Balloon("Pokemon"));
|
||||
Balloon* fred(elaine);
|
||||
daniel = fred;
|
||||
fred = NULL;
|
||||
elaine = cathy;
|
||||
cathy = NULL;
|
||||
|
||||
|
||||
|
||||
//
|
||||
// CHECKPOINT 2B: INSERT NECESSARY EXPLICIT DEALLOCATION
|
||||
//
|
||||
|
||||
|
||||
|
||||
daniel = NULL;
|
||||
elaine = NULL;
|
||||
|
||||
|
||||
// now, with our homemade shared pointer
|
||||
dsSharedPtr<Balloon> cathy2(new Balloon("Buzz Lightyear2"));
|
||||
dsSharedPtr<Balloon> daniel2(cathy2);
|
||||
dsSharedPtr<Balloon> elaine2(new Balloon("Pokemon2"));
|
||||
dsSharedPtr<Balloon> fred2(elaine2);
|
||||
daniel2 = fred2;
|
||||
fred2 = NULL;
|
||||
elaine2 = cathy2;
|
||||
cathy2 = NULL;
|
||||
// NOTE: no explicit destruction required!
|
||||
daniel2 = NULL;
|
||||
elaine2 = NULL;
|
||||
|
||||
|
||||
// ====================================================
|
||||
// SHARED POINTERS WITH INTERCONNECTED STRUCTURES
|
||||
// ====================================================
|
||||
|
||||
Balloon* georgette(new Balloon("Mr Potato Head"));
|
||||
Balloon* henry(new Balloon("Snoopy"));
|
||||
|
||||
georgette->addRope(henry);
|
||||
henry = new Balloon("Tigger");
|
||||
georgette->addRope(henry);
|
||||
georgette->print();
|
||||
henry->print();
|
||||
|
||||
Balloon* isabelle(new Balloon("Shrek"));
|
||||
henry->addRope(isabelle);
|
||||
isabelle = new Balloon("Barney the Purple Dinosaur");
|
||||
georgette->addRope(isabelle);
|
||||
|
||||
henry->print();
|
||||
georgette->print();
|
||||
isabelle->print();
|
||||
|
||||
|
||||
//
|
||||
// CHECKPOINT 2C: REWRITE THE ABOVE EXAMPLE TO USE SHARED POINTERS
|
||||
//
|
||||
|
||||
|
||||
|
||||
// ====================================================
|
||||
// CYCLIC STRUCTURES
|
||||
// ====================================================
|
||||
|
||||
|
||||
// FOR CHECKPOINT 3
|
||||
|
||||
|
||||
/*
|
||||
Balloon* jacob(new Balloon("Dora the Explorer"));
|
||||
Balloon* katherine(new Balloon("Kung Fu Panda"));
|
||||
Balloon* larry(new Balloon("Scooby Doo"));
|
||||
Balloon* miranda(new Balloon("SpongeBob SquarePants"));
|
||||
Balloon* nicole(new Balloon("Papa Smurf"));
|
||||
|
||||
jacob->addRope(katherine);
|
||||
katherine->addRope(larry);
|
||||
larry->addRope(jacob);
|
||||
miranda->addRope(jacob);
|
||||
nicole->addRope(miranda);
|
||||
larry->addRope(nicole);
|
||||
|
||||
katherine = NULL;
|
||||
larry = NULL;
|
||||
miranda = NULL;
|
||||
nicole = NULL;
|
||||
|
||||
// jacob points to a cyclic structure!
|
||||
|
||||
// to cleanup this structure:
|
||||
deleteAll(jacob);
|
||||
|
||||
jacob = NULL;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
std::cout << "end of main" << std::endl;
|
||||
return 0;
|
||||
|
||||
//
|
||||
// NOTE: when smart pointers go out of scope, the destructors for
|
||||
// those objects will be called automatically
|
||||
//
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
58
labs/smart_memory/main_stop_and_copy.cpp
Normal file
58
labs/smart_memory/main_stop_and_copy.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "stop_and_copy.h"
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
StopAndCopy m;
|
||||
std::cout << "TESTING StopAndCopy" << std::endl;
|
||||
std::cout << std::endl << "empty memory:" << std::endl << m;
|
||||
|
||||
// create an interesting data structure
|
||||
m.root = m.my_new('a',MY_NULL,MY_NULL);
|
||||
m.my_new('b',MY_NULL,m.root);
|
||||
m.root = m.my_new('c',m.root,MY_NULL);
|
||||
m[m.root].right = m.my_new('d',m[m.root].left,MY_NULL);
|
||||
|
||||
std::cout << std::endl << "4 cells allocated:" << std::endl << m;
|
||||
|
||||
m.root = m.my_new('e',MY_NULL,m.root);
|
||||
m[m.root].right = m.my_new('f',m[m.root].right,MY_NULL);
|
||||
m[m[m.root].right].right = m.my_new('g',m[m.root].right,MY_NULL);
|
||||
m.root = m.my_new('h',m.root,MY_NULL);
|
||||
m.root = m[m[m.root].left].right;
|
||||
|
||||
std::cout << std::endl << "8 cells allocated:" << std::endl << m;
|
||||
|
||||
// force garbage collection
|
||||
m.collect_garbage();
|
||||
std::cout << std::endl << "after forced garbage collection:" << std::endl << m;
|
||||
|
||||
// allocate more cells to force garbage collection
|
||||
m[m.root].left = m.my_new('i',m.root,MY_NULL);
|
||||
m.root = m.my_new('j',m.root,MY_NULL);
|
||||
m.root = m.my_new('k',m.root,MY_NULL);
|
||||
|
||||
std::cout << std::endl << "after adding 3 more cells:" << std::endl << m;
|
||||
|
||||
|
||||
// Walk through the Stop And Copy garbage collection algorithm on
|
||||
// the memory at this point in the program. Draw a pencil & paper
|
||||
// diagram to show your work.
|
||||
|
||||
|
||||
// UNCOMMENT THESE LINES AFTER YOU FINISH CHECKPOINT 1 (to check your work)
|
||||
/*
|
||||
m.root = m.my_new('l',m.root,MY_NULL);
|
||||
std::cout << std::endl << "adding another cell triggers automatic garbage collection:" << std::endl << m;
|
||||
|
||||
// "forget" the root pointer
|
||||
m.root = MY_NULL;
|
||||
m.collect_garbage();
|
||||
std::cout << std::endl << "root set to null & forced garbage collection:" << std::endl << m;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
96
labs/smart_memory/stop_and_copy.cpp
Normal file
96
labs/smart_memory/stop_and_copy.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "stop_and_copy.h"
|
||||
|
||||
|
||||
// Return the node corresponding to a particular address
|
||||
Node& StopAndCopy::operator[](Address addr) {
|
||||
if (addr == MY_NULL) {
|
||||
std::cerr << "ERROR: NULL POINTER EXCEPTION!" << std::endl; exit(1); }
|
||||
if (addr < OFFSET || addr >= OFFSET+CAPACITY) {
|
||||
std::cerr << "ERROR: SEGMENTATION FAULT!" << std::endl; exit(1); }
|
||||
return memory[addr-OFFSET];
|
||||
}
|
||||
|
||||
|
||||
Address StopAndCopy::my_new(char v, Address l, Address r) {
|
||||
// if we are out of memory, collect garbage
|
||||
if (next == partition_offset+CAPACITY/2) {
|
||||
collect_garbage();
|
||||
// update the addresses (since memory has been shuffled!)
|
||||
if (l != MY_NULL) l = memory[l-OFFSET].left;
|
||||
if (r != MY_NULL) r = memory[r-OFFSET].left;
|
||||
}
|
||||
// if we are still out of memory, we can't continue
|
||||
if (next == partition_offset+CAPACITY/2) {
|
||||
std::cerr << "ERROR: OUT OF MEMORY!" << std::endl; exit(1); }
|
||||
// assign the next available node
|
||||
memory[next].value = v;
|
||||
memory[next].left = l;
|
||||
memory[next].right = r;
|
||||
return OFFSET + next++;
|
||||
}
|
||||
|
||||
|
||||
// Print function for debugging
|
||||
std::ostream& operator<<(std::ostream &ostr, StopAndCopy &m) {
|
||||
ostr << "root-> " << m.root << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr.width(4); ostr << i+OFFSET << " "; }
|
||||
ostr << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr << " "; ostr.width(1); ostr << m.memory[i].value << " "; }
|
||||
ostr << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr.width(4); ostr << m.memory[i].left << " "; }
|
||||
ostr << std::endl;
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
ostr.width(4); ostr << m.memory[i].right << " "; }
|
||||
ostr << std::endl;
|
||||
// print "FREE" or "used" for each node in the current partition
|
||||
for (int i = 0; i < CAPACITY; i++) {
|
||||
if (i >= m.next && i < m.partition_offset+CAPACITY/2)
|
||||
ostr << "FREE ";
|
||||
else if (i >= m.partition_offset && i < m.partition_offset+CAPACITY/2)
|
||||
ostr << "used ";
|
||||
else // print nothing for the other half of memory
|
||||
ostr << " "; }
|
||||
ostr << std::endl;
|
||||
return ostr;
|
||||
}
|
||||
|
||||
|
||||
void StopAndCopy::collect_garbage() {
|
||||
// switch to the other partition
|
||||
partition_offset = (partition_offset == 0) ? CAPACITY/2 : 0;
|
||||
// scan & next start at the beginning of the new partition
|
||||
Address scan;
|
||||
next = scan = partition_offset;
|
||||
// copy the root
|
||||
copy_help(root);
|
||||
// scan through the newly copied nodes
|
||||
while (scan != next) {
|
||||
// copy the left & right pointers
|
||||
copy_help(memory[scan].left);
|
||||
copy_help(memory[scan].right);
|
||||
scan++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StopAndCopy::copy_help(Address &old) {
|
||||
// do nothing for NULL Address
|
||||
if (old == MY_NULL) return;
|
||||
// look for a valid forwarding address to the new partition
|
||||
int forward = memory[old-OFFSET].left;
|
||||
if (forward-OFFSET >= partition_offset &&
|
||||
forward-OFFSET < partition_offset+CAPACITY/2) {
|
||||
// if already copied, change pointer to new address
|
||||
old = forward;
|
||||
return;
|
||||
}
|
||||
// otherwise copy it to a free slot and leave a forwarding address
|
||||
memory[next] = memory[old-OFFSET];
|
||||
memory[old-OFFSET].left = next+OFFSET;
|
||||
old = next+OFFSET;
|
||||
next++;
|
||||
}
|
||||
|
||||
53
labs/smart_memory/stop_and_copy.h
Normal file
53
labs/smart_memory/stop_and_copy.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <iostream>
|
||||
|
||||
// size of memory available for this process
|
||||
#define CAPACITY 16
|
||||
// first valid address for this process
|
||||
#define OFFSET 100
|
||||
#define MY_NULL 0
|
||||
|
||||
|
||||
typedef int Address;
|
||||
|
||||
|
||||
// A helper class for the StopAndCopy memory system
|
||||
class Node {
|
||||
public:
|
||||
Node() { value='?'; left=-1; right=-1; } // initialized with "garbage" values
|
||||
char value;
|
||||
Address left;
|
||||
Address right;
|
||||
};
|
||||
|
||||
|
||||
// A simple implementation of the basic StopAndCopy garbage collector
|
||||
class StopAndCopy {
|
||||
public:
|
||||
StopAndCopy() {
|
||||
root = MY_NULL;
|
||||
partition_offset = 0;
|
||||
next = 0;
|
||||
}
|
||||
// Return the node corresponding to a particular address
|
||||
Node& operator[](Address addr);
|
||||
// allocate a new node
|
||||
Address my_new(char v, Address l, Address r);
|
||||
// a print function for debugging
|
||||
friend std::ostream& operator<<(std::ostream &ostr, StopAndCopy &m);
|
||||
// force automatic memory management
|
||||
void collect_garbage();
|
||||
// REPRESENTATION
|
||||
public:
|
||||
// the user must set this value such that all useful memory is
|
||||
// reachable starting from root (NOTE: publicly accessible)
|
||||
Address root;
|
||||
private:
|
||||
// total machine memory
|
||||
Node memory[CAPACITY];
|
||||
// which half of the memory is active
|
||||
int partition_offset;
|
||||
// next available node
|
||||
int next;
|
||||
// a private helper function
|
||||
void copy_help(Address &old_address);
|
||||
};
|
||||
Reference in New Issue
Block a user