views:

447

answers:

7

NOTE: THIS IS NOT HOMEWORK IT IS FROM A PRACTICE EXAM GIVEN TO US BY OUR PROFESSORS TO HELP US PREPARE FOR OUR EXAM

I'm currently studying for a programming exam. On one of the sample tests they gave us we have the following question:

Suppose you have been given a templated Container that holds an unordered collection of objects.

template <typename T>
class Container {
   public:
      void insert(T *op);
      // EFFECTS: inserts the object pointed to by op into
      // the container
      T *remove();
      // EFFECTS: removes an object from the Container, and
      // returns a pointer to it. Returns NULL if no
      // objects remain in the Container.
      // Note: the implementation can choose which
      // object to return if more than one exists.
      Container(); // ctor
      Container(const Container &l); // copy ctor
      Container &operator=(const Container &l); // assignment
      ~Container(); // dtor
   private:
      ...
};

Note that this is the interface only; the implementation details have been left out for brevity. However, you may assume that the implementation is node based; a linked collection of nodes hold objects.

You suspect that the implementation of the destructor does not satisfy the Conservation Rule of the At-Most-Once invariant, and is leaking memory instead. Write an acceptance test (similar to those in Project 4) to check for this condition. You must supply a suitable contained type, and a main that performs the test.

Note that you cannot depend on any behavior that the language leaves undefined, you may not assume that you have the altnew allocator from Project 5 available to you, and you may not override the delete operator. Hint: you are allowed to use a global variable.

I though something like:

#include <iostream>

using namespace std;

int *p = NULL;

void leak() {
    int *num = new int(5);
    p = num;
    delete num;
}

int main() {
    if ((*p = 6)) {
        cout << "Memory leak\n";
    } else {
        cout << "No Leak\n";
    }
}

The basic idea behind this is I though I couldn't write to a space of memory that I hadn't allocated. In compiling this test code though it works just fine so apparently you can. Any ideas on how to write such a test case though?

+5  A: 

When you say:

void leak() {
    int *num = new int(5);
    p = num;
    delete num;
}

there is no memory leak. There is however, a dangling pointer (p) which will cause undefned behaviour if dereferenced.

anon
Right. You see I thought that when you used the delete command then it would deallocate and you would no longer be able to write to that memory address. I tested my program by compiling it with and without the delete num program but in both cases it said there was no memory leak. For the actual exam I'd have to use the methods given to us but this code was just to test the concept which apparently isn't valid.
blcArmadillo
delete num program = delete num line
blcArmadillo
Once deleted, if you write to the memory you have what C++ calls "undefined behaviour" - anything could happen. Your code could even appear to work. But there is no leak.
anon
A: 

I'm not sure what is listed in Project 4 and Project 5, but I think the way to do this is to assign a global pointer (as per the hint) to the object that you insert in the container. If you then destroy the container, it should destroy the objects within it, and that global pointer should now be null.

aronchick
No. The global pointer will not be affected by the deleting the Container, it would just be dangling and should fail if de-referenced - which might be what's wanted. Implementation is left as an exercise to the student. :-)
Bob Jarvis
That is what I meant - test to see if it fails. If it does not fail, then it's leaky.
aronchick
A: 

You can increment an integer global variable in the T constructor, and decrement it in the T destructor: doing this will tell you whether T instances are being destroyed by the container. The acceptance test can allocate a few T instances into a Container instance, destroy the Container, and test whether that destroyed the T instances (i.e. whether the T destructors were invoked).

Without overriding the delete operator I don't see an easy way to tell whether the container is not only destroying T instances but also releasing the memory which the T instances occupy: but if it is destroying T instances (which you can test as mentioned in the first paragraph above) then you might like to hope that it's probably also releasing the memory occupied by each instance.

ChrisW
+4  A: 

What if you create a class to use as the template parameter that will add 1 to a global variable in it's constructor and decrease that same global variable by 1 in it's destructor.

Then you can perform whatever tests you want on the container (create it, fill it and empty it, delete it, etc) and check for memory leaks by checking that the global variable is 0 after the container has been destroyed.

C Nielsen
A: 

I'd use a contained type that keeps a reference count. When you insert the item in the container, the reference count should be incremented. When you destroy the container, it should be decremented back to its starting value. The reference count could be held in either a global variable (as suggested) or a static variable inside the class (I'd normally use the latter, but it is slightly more complex).

Jerry Coffin
Thanks for the info. Do you mean adding that code in the implementation of the insert and deconstruction method? If so I don't think I can do this because I'm not writing the method implementations.
blcArmadillo
No -- this would all be in the object being stored. Normally it's done in that class' ctors and dtor -- each ctor increments the count, and the dtor decrements the count. Another possibility is to have the ctors and dtors print strings saying when they run.
Jerry Coffin
A: 

eecs 280, hell yeah

the forums on ctools has a thread on it, good luck on the exam

jh
+4  A: 

You could use an element class like this one, which counts it's instances:

 class InstCounter {
 public:
   static int counter;
   InstCounter() { counter++; }
   ~InstCounter() { counter--; }
 };
 int InstCounter::counter = 0;

 int main(int argc, char** argv)
 {
   { Container<InstCounter> c;
     // insert elements...
     c.insert(new InstCounter);
   } // calls dtor of c
   if (InstCounter::counter > 0)
     std::cout << "Container is leaking." << std::endl;
   return 0;
 }
Wolfgang Plaschg
This is along the lines of what I was thinking. But the use of a static member variable is probably more elegant than my suggestion of a global variable. Plus Wolfgang actually wrote out the code :) So use this one!
C Nielsen