COSC 502: Objects and Algorithms

Project 5
Spring 2011

Due: Mon, 5/2 @ 5 PM
10 points

For Project 4, we used the C++ vector class from the Standard Template Library to implement the Payments class, which let us store and manipulate Payment objects in memory. Many different types of container classes exist, and it is important to know how to use them, but it is also important to know how to implement them.

For this project, we are going to write our own container class for storing and manipulating Payment objects. Rather than using a C++ vector to implement Payments, we are going to implement Payments using a singly linked list.

As you know from lecture, a singly linked list consists of a collection of nodes linked together using pointers. The class definition for the Node class for storing Payment objects is:

class Node
{
  public:
    Node( Payment payment = Payment() );
    void setPayment( Payment newPayment );
    Payment getPayment() const;
    void setNextPtr( Node* newNextPtr );
    Node* getNextPtr() const;

  private:
    Payment payment;
    Node* nextPtr;

}; // Node class
The class definition for the Payments class is:
class Payments
{
  public:
    Payments();
    void add( const Payment& payment );
    double getTotal() const;
    void print() const;
    void printInternal() const;
    void read( string filename );
    void read( istream& in );
    void remove( int number );
    void write( string filename ) const;
    void write( ostream& out ) const;
    ~Payments();

  private:
    Node* payments;
    // ...

    bool empty() const;
    void clear();

}; // Payments class

With the exception of printInternal and the destructor ~Payments, the public interface for this version of Payments is identical to that of the version of Payments for Project 4. There are two additional private methods, empty and clear, which are analogous to the methods vector<T>::empty and vector<T>::clear.

Payments::printInternal is a method that we'll discuss in class. It should prove useful for development and testing because it will let you inspect the internal representation of the linked list.

Since you are writing your own container for this project, there are no functions available to sort and find payments in the list of payments. You will have to implement this functionality as part of the add and remove methods. That is, the add method should insert a new payment into the linked list in order based on its number, provided that a payment with the same number does not already exist in the list. The remove method should remove a payment based on its number and report if no such payment exists.

Be sure to incrementally develop and test. Start by implementing and testing Node. Make sure Node works completely before starting Payments.

I would recommend developing and testing Payments by writing test cases in main. I would not recommend trying to develop and test Payments through HSBCApp until you're sure that Payments is working properly. If you implement Payments properly, the HSBCApp class should require no modifications. For this project, you do not need to write doc comments.

As you're developing and testing Payments, you will be referencing and manipulating memory directly. As a consequence, when you make a mistake, your program is going to crash and dump core. Even if your program does not crash, you may have a memory leak.

Two tools that can help you investigate and fix core dumps and memory leaks are the GNU debugger (gdb) and valgrind. Before using these tools, you must compile using the -g switch, which makes the compiler include debugging information in the executable a.out. To compile your project, execute the command:

seva% g++ -g p5.cc
Once you have compiled with the -g option, to test if a running program has memory leaks, run valgrind by executing the command:
seva% valgrind a.out
When you do this, you'll see a bunch of output from valgrind as well as output from your program. At the end of this output, you will see something like:
==20551== HEAP SUMMARY:
==20551==     in use at exit: 0 bytes in 0 blocks
==20551==   total heap usage: 6 allocs, 6 frees, 150 bytes allocated
==20551== 
==20551== All heap blocks were freed -- no leaks are possible
==20551== 
==20551== For counts of detected and suppressed errors, rerun with: -v
==20551== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 18 from 7)
Notice that the output indicates that there were six allocations and six deallocations (frees), that no leaks are possible, and that there were zero errors. If your program is doing something wrong with memory, valgrind will tell you. When there is a problem, you can run valgrind so it does a full leak check:
seva% valgrind --leak-check=full a.out
With this option, valgrind will tell you the line number of your program where a problem has occurred.

When your program crashes, it should write a file named core in your current directory. (If your program crashes and does not produce a core file, then you may not have copied over my account settings from hw2.) A core file is a snapshot of the program and the memory in which it was running when it crashed. Debuggers can use information in the core file to help you figure out what went wrong.

When your program dumps core, to find the line of your program on which it crashed, run gdb on a.out and core and use gdb's backtrace command. For example,

seva% g++ -g p5.cc
seva% a.out
349 10/10/2011 "1789" 6.65
350 10/11/2011 "Tombs" 34.65
351 10/12/2011 "Wiseys" 12.65
0 10/10/2011 "1789" 6.65
350 10/11/2011 "Tombs" 34.65
351 10/12/2011 "Wiseys" 12.65
--
Found no payment with number 349
0 10/10/2011 "1789" 6.65
350 10/11/2011 "Tombs" 34.65
351 10/12/2011 "Wiseys" 12.65
*** glibc detected *** a.out: double free or corruption (fasttop): 0x095d8050 ***

Abort (core dumped)
seva% ls
a.out*  core  p5.cc  payments.dta  payments.out
seva% gdb a.out core
GNU gdb (GDB) 7.1-ubuntu
...
<lots of uninterpretable output>
...
Program terminated with signal 6, Aborted.
#0  0x0057f422 in __kernel_vsyscall ()
(gdb) backtrace
#0  0x0057f422 in __kernel_vsyscall ()
#1  0x0013a651 in *__GI_raise (sig=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2  0x0013da82 in *__GI_abort () at abort.c:92
#3  0x0017149d in __libc_message (do_abort=2, 
    fmt=0x245f98 "*** glibc detected *** %s: %s: 0x%s ***\n")
    at ../sysdeps/unix/sysv/linux/libc_fatal.c:189
    #4  0x0017b591 in malloc_printerr (action=<value optimized out>, 
    str=0x6 <Address 0x6 out of bounds>, ptr=0x9fba050) at malloc.c:6266
#5  0x0017cde8 in _int_free (av=<value optimized out>, p=<value optimized out>)
    at malloc.c:4794
#6  0x0017fecd in *__GI___libc_free (mem=0x9fba050) at malloc.c:3738
#7  0x0032f741 in operator delete(void*) () from /usr/lib/libstdc++.so.6
#8  0x0804a1fb in Payments::clear (this=0xbf8758b0) at p5.cc:444
#9  0x0804aa6f in ~Payments (this=0xbf8758b0, __in_chrg=<value optimized out>)
    at p5.cc:553
#10 0x0804b21f in main () at p5.cc:638
(gdb) quit
seva%
Reading from the bottom up, notice that Record #8 is the last record that references my source file p5.cc. Also notice that Record #8 indicates that it was executing the Payments::clear method and line 444 of p5.cc. That was the line of my program that ultimately lead to the crash. Now, that is where the program crashed, but that is not necessarily the line that caused the crash. It turns out that my program crashed, not because a problem with the clear method, but because of a problem with how my program constructed the list being cleared.

Core files can be quite big. When you produce one and you're finished with it, remove it.

Instructions for Submission

At the top of the file containing your source code (i.e., the file containing the C++ instructions), place the following header comment above the Doc comment documenting your class with the appropriate modifications:
//
// COSC-071 Project 5
// Name: <your name>
// E-mail: <e-mail address>
// Instructor: Maloof
// Spring 2011
//
// In accordance with the class policies and Georgetown's Honor Code,
// I certify that, with the exceptions of the lecture notes and those
// items noted below, I have neither given nor received any assistance
// on this project.
//

When you are ready to submit your program for grading, use the submit program, just like you did for p4. Both submit.jar and the file containing your program should be in the same directory. You can confirm this by listing the files in your home directory:

seva% ls
p5.cc    submit.jar
You may see other files from previous assignments in your home directory, but p5.cc and submit.jar should be among them.

This assignment's label is p5. Assuming the file you want to submit is p5.cc, then to submit you enter the command

seva% java -jar submit.jar -a p5 -f p5.cc
Make sure you submit the file containing your program's source, not its executable code; that is, do not submit a.out.

Once you've submitted your project, it is a good idea to keep a copy on seva, which preserves the modification date and time and ensures that you will have a backup copy of your project. If we lose your project or submit breaks, then we will need to look at the modification date and time of your project to ensure that you submitted it before it was due.

The TAs who will be grading your projects this semester are listed on the main page. You must submit your project before 5 PM on the due date.