Starting from:

$29.99

COMP2012 Assignment 3 Solution

As an example to demonstrate the ownership problem and the application of the smart pointers we just implemented, the second half of this assignment will be an implementation of a simple representation of graphs and operations on them. Since a node can be the neighbor of, and hence have its reference held by multiple other nodes, smart pointers can be used to keep track of the number of held references of a node, and deallocate a node when no other nodes hold a reference to it.
Download
Skeleton code: pa3-skeleton.zip
End of Download
Review
We first review graphs, which is used as an example to demonstrate the ownership problem in this assignment.
A graph is a structure consisting of a set of nodes and a set of edges, which are pairs of nodes. In this assignment, we consider undirected graphs only, where an edge from a node n1 to another node n2 is also an edge from n2 to n1. Additionally, self-edges (an edge from a node to itself) and duplicate edges are forbidden.
If an edge from n1 to n2 exists, we say that n2 is a neighbor of n1. To represent a graph, we store the adjacency list of each node, which is simply a list of its neighbors. The degree of a node is the number of its neighbors.
A node nk is said to be reachable from a node n0 if a sequence of edges (n0, n1), (n1, n2), ..., (nk-1, nk) exists. A connected subgraph of a node n is the set of reachable nodes from n.
As an example of graphs, think of the flights of an airline. The airports served by the airline are represented as nodes, and flights between two airports are represented as edges. The degree of a node (an airport) would then be the number of other airports connected by a direct flight. An airport is reachable from another airport if a sequence of flights connects them. And the connected subgraph of an airport is the set of airport reachable by an arbitrary number of flights. Natually, the airline would prefer not to waste storage space for airports they no longer serve, so this is a perfect application of our smart pointers, which would deallocate the data of an airport when all flights to it have been discontinued (i.e. all edges of the node have been removed).
End of Review
Description
This assignment can be divided into three parts, with each depending on the previous one. The first two parts each consists of a class template, and the last part consists of some function templates that operate on the class templates. You should ensure that memory leak does not occur in all of the functions you implement.
Part (A) SmartPtr<T>
The class template SmartPtr<T> declared in smartptr.h provides an abstraction for handling memory allocation and deallocation using reference counting. It contains the following data members:
T* ptr; unsigned int* count;
ptr is a pointer to type T. If the address stored in ptr is nullptr, we say that this SmartPtr is null. If ptr is not nullptr, it should point to a valid, allocated address. Note that a null SmartPtr only conceptually represents a null pointer, and the SmartPtr object itself is still defined. All member functions beside operator* can still be safely called on a null SmartPtr.
count is a pointer to type unsigned int, which is used to store the number of SmartPtr instances containing the address ptr. count should be nullptr if and only if ptr is nullptr.
Memory management of the object referred to by SmartPtrs are managed by the constructor, destructor, set and unset member functions. The only ways to initialize and assign ptr is by setting it to nullptr, an address allocated by copy-constructing an instance of type T, or the address contained in another instance of SmartPtr.
When a SmartPtr is created for or set to a new T object, count should be allocated to store the number 1, meaning, the address of the new T object is stored in only 1 SmarPtr. For example:
SmartPtr sp {42};
The number pointed to by count of sp is now 1 and ptr is allocated to store 42.
sp.set(0);
Again, the number pointed to by count of sp is set to 1 and ptr is allocated to store 0.
When a SmartPtr is copied by the copy constructor or assignment operator, the address stored in ptr is being copied, the number pointed to by count has to be incremented by 1. It means that the T object pointed by to ptr is being pointed to by one more SmartPtr instance.
Similarly, when a SmartPtr is destructed or set to refer to another object, the number pointed to by count has to be decremented. When the number becomes zero, it means that this instance of SmartPtr is the last to hold a reference to the object pointed to by ptr. Both ptr and count should be deallocated properly.
Member functions
SmartPtr<T>::SmartPtr

Compares the pointer member ptr of two instances of SmartPtr using the corresponding operators.
SmartPtr<T>::operator*
T& operator*() const;
Returns by reference the object pointed to by ptr. Whether ptr points to nullptr is not checked.
SmartPtr<T>::operator->
T* operator->() const;
Returns the address stored in ptr.
Non-member functions operator<<
template <typename T> std::ostream& operator<<(std::ostream& os, const SmartPtr<T>& sp);
If sp is not null, outputs to os the string "SmartPtr(", followed by the object pointed to by sp.ptr, then by a ",", then by the number of SmartPtr instances containing the address sp.ptr, finally by ")". The object pointed to by sp.ptr should be output by invoking operator<<. Otherwise, outputs to os the string "SmartPtr()".
For example, if sp.ptr points to a string "Hello World", with two instances of SmartPtr holding the same address, os << sp should output "SmartPtr(Hello World,2)" to os.
The return value should follow the convention as the standard library, where chaining multiple invocations of operator<< outputs to the same stream. For example, os << sp << " " would output a newline character following "SmartPtr(Hello World,2)" to os.
Implement this function template in smartptr-output.tpp. No other required implementations should be present in smartptr-output.tpp. For grading, besides the cases explicitly testing operator<<, your implementation in smartptr-output.tpp will be swapped for a standard implementation.
Part (B) Node<T>
The class template Node<T> represents a node or vertex in a graph. It contains the following data members:
T val;
SmartPtr<Node<T>>* out; unsigned int capacity; unsigned int size_p;
Member functions
Node<T>::Node
Node(const T& val);



template <typename T> void remove_graph(SmartPtr<Node<T>> root);
Removes every node from the connected subgraph of root by removing all edges between each pair of nodes, which would deallocate their memory if no other SmartPtrs hold a reference to them. Does nothing if root is null.
find
template <typename T>
SmartPtr<Node<T>> find(SmartPtr<Node<T>> root, T val);
Finds a node whose val field is equal to val. Returns a null SmartPtr if none of the nodes have a val field is equal to val, or if root is null.
reachable
template <typename T> bool reachable(SmartPtr<Node<T>> root, SmartPtr<Node<T>> dest);
Returns true if dest is reachable from root, i.e. dest is in the connected subgraph of root, and false otherwise. Returns false if either root or dest is null.
End of Description
Tasks
Your task is to implement the missing member functions of the two classes and function templates in smartptr.tpp, smartptr-output.tpp, graph.tpp and graph-output.tpp.
End of Tasks
Sample Output and Grading
There are 27 given test cases of which the code can be found in the given main function.
These 27 test cases are first run without any memory leak checking (they are numbered #1 #27 on ZINC). Then, the same 27 test cases will be run again, in the same order, with memory leak checking (those will be numbered #101 - #127 on ZINC). For example, test case #108 on ZINC is actually the given test case #8 (in the given main function) run with memory leak checking.
About memory leak and other potential errors
End of Sample Output and Grading
Submit a single zip file pa3.zip containing the following files only: smartptr.tpp, smartptroutput.tpp, graph.tpp and graph-output.tpp. Submit the file to ZINC. ZINC usage instructions can be found here.
Notes:
In the grading report, pay attention to various errors reported. For example, under the "make" section, if you see a red cross, click on the STDERR tab to see the compilation errors. You must fix those before you can see any program output for the test cases below.
Make sure you submit the correct file yourself. You can download your own file back from ZINC to verify. Again, we only grade what you uploaded last to ZINC.
Compilation Requirement
It is required that your submissions can be compiled and run successfully in our online autograder ZINC. If we cannot even compile your work, it won't be graded. Therefore, for parts that you cannot finish, just put in dummy implementation so that your whole program can be compiled for ZINC to grade the other parts that you have done. Empty implementations can be like:


In this particular PA, it is probably related to misuse of dynamic memory. Good luck with bug hunting!
Specification clarification
Q: Should I increment the count when I copy construct or copy assign a SmartPtr?
End of Frequently Asked Questions

More products