views:

890

answers:

11
typedef struct temp  
{  
        int a,b;  
        char  *c;  
        temp(){ c = (char*)malloc(10);};  
        ~temp(){free(c);};  
}temp;  

int main()  
{  
   temp a;  
   list<temp>   l1;  
   l1.push_back(a);  
   l1.clear();  
   return 0;  

}

giving segmentation fault.

A: 

Misread question while it was initially being added (answer withdrawn).

Nick Josevski
There's a default constructor for both temp and list, so they are initialised.
anon
Nick, to avoid more down votes, you should delete your answer - if you can. if you can't edit it and just say something like "This answer was incorrect" and remove the incorrect portions.
Binary Worrier
Thanks for the tip Binary Worrier.
Nick Josevski
+26  A: 

You don't have a copy constructor.

When you push 'a' into the list, it gets copied. Because you don't have a copy constructor (to allocate memory for c and copy from old c to new c) c is the same pointer in a and the copy of a in the list.

The destructor for both a's gets called, the first will succeed, the second will fail because the memory c points to has already been freed.

You need a copy constructor.

To see whats happening, put some couts in the constructors and destructors and step through the code.

Binary Worrier
Can who ever downvoted let me know why, if there's something wrong with my answer I'd like to know what it is. Thanks guys
Binary Worrier
I didn't -1 you, but c doesn't get deleted - deleting an object and freeing memory are not the same thing.
Pete Kirkham
@Pete: You are of course correct, my fingers misspoke for me, thanks :)
Binary Worrier
It would be nice to mention the use of a swap idiom in the copy ctor to avoid extra memory allocations.
Dave Mooney
A: 

You need a copy constructor & an assignment operator - things stored in an STL collection are stored as copies and can be re-assigned as the vector changes size.

It's difficult to see from your code exactly what the semantics of the copy constructor should be, but at minimum it's cot to allocate some memory so that (if nothing else) the destructor has something to free. The assignment operator is equally difficult to specify without more details of your class.

anon
+4  A: 

You need a deep-copy constructor to avoid double free(). You have a variable of temp class (a), then you add it to the list. The variable is copied. Then you clear the list, the element inside is destroyed and free() is called. Then a variable is destroyed and free() is called again for the same address which leads to segmentation fault.

You need a copy constructor for deep copying class temp variables which would malloc() another buffer and copy data.

sharptooth
+3  A: 

At the time when you call l1.push_back(a) a second copy of 'a' is copy-constructed. As a result there are now two classes that believe they own the memory from the original malloc call, and when the second is deleted it will try to free memory deleted by the first.

The solution is to add a copy constructor that deals with the fact that instance of the class does not actually own the data. Typically you would do this by having some form of reference count.

Andrew Grant
A: 

You need to define a copy constructor for temp. Right now what happens when you push a into the list is a copy of a is made. The copy (call it a2) is initialized by saying a2.c = a.c. This means both a and a2 point to the same block of memory. When their destructors are called, that block of memory is free'd twice, leading to the segmentation fault.

Given what you've posted, the copy constructor should probably be something like this:

temp::temp (temp const &rhs)
{
    this->a = rhs.a;
    this->b = rhs.b;
    this->c = (char *) malloc (10);
    memcpy (this->c, rhs.c, 10);
}

This assumes that what c points to is always 10 chars long...

Sol
+1  A: 

If you don't want to add copy constructor you can consider list of pointers to values instead of list of values.

list<temp*>   l1;
l1.push_back( new temp() );

But then you have to delete manually each object in list to prevent memory leak.

Also a,b members in your struct are not initialized. Be careful.

Mykola Golubyev
+3  A: 

Apart from the fixes given, you should avoid using malloc/free in C++. In your particular case, i'd go with a vector :

#include <vector>

 typedef struct temp  
{  
        int a,b;  
        std::vector<char> c;  
        temp(){ c.reserve(10);};  
}temp;  

int main()  
{  
   temp a;  
   list<temp>   l1;  
   l1.push_back(a);  
   l1.clear();  
   return 0;  

}
Benoît
That's an unfortunate example. Someone who doesn't know STL well could assume that reserve() changes vector's size (actually adds elements) while it really only allocates a buffer.
sharptooth
I know it does ; i did that because i assumed he wanted to use push_back commands.
Benoît
+1  A: 

In addition to the copy constructor, it is wise to provide an = operator too in this case.

struct temp {   // typedef is implicit in C++
  int a,b;
  char * c;

  // Constructor
  temp() { c = malloc(10); }
  // Destructor
  ~temp() { free(c); }
  // Copy constructor
  temp(const temp & x) { c = malloc(10); setTo(x); }
  // Operator =
  temp & operator = (const temp & x) { setTo(x); return *this; }

  // Initialize THIS to X
  void setTo(const temp & x) { a = x.a; b = x.b; memcpy(c,x.c,10); }
};
Eric Bainville
A: 

Another problem with this code are extra semicolons in

temp(){ c = (char*)malloc(10);};
~temp(){free(c);};

it is better to remove them:

temp(){ c = (char*)malloc(10);}
~temp(){free(c);}
dmityugov
A: 

How ironic that this had the "STL" tag but the lack of STL is what's causing the problem.

Jimmy J
What "lack of STL" would that be?
anon
Use of STL would save you from calling malloc()/free(). You also wouldn't need any destructors or copy constructors thus mooting most of the solutions being posted.
Jimmy J