views:

59

answers:

1

All right, you guys were very helpful with my last question, so I'll try another one. This is also homework and while the last one was quite old, this has been submitted and is waiting to be marked. So if there's anything that will bite me it'll probably be this problem. I've obfuscated the class names and such since it's still possible to submit the assignment (for other students).

I have a class whose only member is a pointer to an Object. This class is constructed to expose certain operations from the pointer it is currently holding - the Object *o_ which is a base class to Object{1, 2, 3, ...}. Now, I'm able to do the following without any memory leaks or crashes.

std::vector<ObjectPtr> v;
v.push_back(ObjectPtr(new Object1(..., ..., ...)));
v.push_back(ObjectPtr(new Object2(..., ...)));
v.push_back(ObjectPtr(new Object1(.., .., ..)));

// Copy Constructor Ptr
std::vector<ObjectPtr> v2(v);
// Assignment Operator Ptr
std::vector<ObjectPtr> v3;
v3 = v2;

All this works, and there are no memory leaks. But if I try to read in the contents from a file with a istream_iterator<ObjectPtr> it starts to leak. ObjectPtr is the only class handling dynamic memory and the Object *o_ is either set to NULL or allocated by Object{1, 2, 3, ...}.

The file to be read looks like this

Object1
...
...
Object2
...
...
Object1
..
std::ifstream is("file.txt");
std::istream_iterator<ObjectPtr> in(is), end;
for (; in != end; ++in)
    cout << *in << "\n";

The friend function in ObjectPtr used to read in these values looks like

friend istream &operator>>(istream &is, ObjectPtr &op) {
    std::string tmp;
    while (std::getline(is, tmp)) {
        if (tmp == "Object1") {
            op.o_ = new Object1;
            return is >> (Object1 &)*(op.o_); // Send it to operator>> for Object1
        }
        if (tmp == "Object2") {
            op.o_ = new Object2;
            return is >> (Object2 &)*(op.o_);
        }
        ...
    }
    return is;
}

Somewhere here it starts unicorning on me, and I'd really like to know why.

In a nutshell - istream_iterator leaks memory while assignment and copy constructor works properly, which leads me to believe that the classes Object{1, 2, 3, 4, ..} are constructed correctly, and the problem is to be found within operator>>.

+2  A: 

Here's the first thing occurring to me. I don't know whether that's the issue you're hunting for:

friend istream &operator>>(istream &is, ObjectPtr &op) {
    std::string tmp;
    while (std::getline(is, tmp)) {
        if (tmp == "Object1") {
            op.o_ = new Object1;

In that last line, what happens to the old value in op.o?
Remember, streaming into an object means to stream into a fully constructed object and you have to mind the object's old data. (That's why often prefer constructors taking std::istream. For complex objects that safes the initialization for an object that is to be changed in the very next moment.)

Does ObjectPtr have an assignment operator or a swap() member function? If so, it might be easier to implement the input operator by constructing a new object and assign it to/swap it with op.

sbi
Oh, man.. You made my day - delete op.o_; Thank you, thank you, thank you!
citizencane
@citizencane: Note that you still have to watch out for that `o` member in many other places. What about assignment? Copy-construction? Really, I would rather implement `operator>>()` in terms of other members (as I had written in my answer) than duplicating all that code manually fiddling with a dynamically allocated object.
sbi
Thinking about this again, I might even tear `ObjectPtr` apart into two classes, since it does (at least) two things: (A) it manages a dynamically allocated object and (B) it serves as a proxy in dealing with this object. If you separate (A) into a suitable smart pointer class, implementing (B) becomes much easier and cleaner.
sbi
ObjectPtr does have an assignment operator and copy constructor, which both do work (see above), I thought that reading the stream would result in the Object *o being set to NULL (through the default constructor), so I could just allocate it without worrying about freeing memory. Too bad I can't re-submit my assignment. :-) Well, I've learned a lot today. Thank you!
citizencane