I am trying to make a deep copy (for copy on write) of an object but I get a segmentation fault.
I am using a hashtable with linked list.
class Person
{
public:
Person(const char * id,int nb)
{
this->id=strdup(id);
this->nb=nb;
this->init=init;
this->next=NULL;
}
Person(const Person& rhs) :
nb(rhs.nb),
init(rhs.init),
id(strdup(rhs.id)),
next(rhs.next == NULL ? NULL : new Person(*rhs.next)) {}
char* strdup(char const* in)
{
char* ret = new char[strlen(in)+1];
strcpy(ret, in);
return ret;
}
int nb,init;
const char * id;
Person *next;
};
Hashtable deepcopy (const Hashtable& rhs)
{
num[0]=num[0]-1;
Person** array=rhs.table;
Hashtable autre;
for (int i = 0 ; i < size; ++i)
if (autre.table[i]!=NULL)
autre.table[i] = new Person(*array[i]);
return autre;
num[0]=1;
}
the attributs of my class Hashtable:
Person **table;
int* num;
EDIT: this problem seem to be fixed.
What is wrong with my deep copy? I don't understand. I think that my copy constructor is good but I don't understand why I get a seg fault when I run it.
This code must be fixed:
for (int i = 0 ; i < size; ++i)
autre.table[i] = new Person(*array[i]);
table has fixed size, and it's filled with null-pointers. In your loop, you don't check if the element to be copied is a null-pointer, so you derefence it and try to copy the entity which even doesn't exist.
for (int i = 0 ; i < size; ++i) {
if(array[i] != NULL) {
autre.table[i] = new Person(*array[i]);
}
}
PS: It's better to use nullptr instead of NULL.
Problems that I see:
Default constructor of Person.
Person(const char * id,int nb)
{
this->id=id;
this->next=NULL;
}
If I use
Person foo()
{
char id[] = "John";
return Person(id, 0);
}
Person a = foo();
Then the stack memory used for holding "John" in foo is now held on to by a, which will lead to undefined behavior.
You need to take ownership of the input string. Use std::string for id instead of char const*.
Copy constructor of Person.
The statement
id(rhs.id),
will be a problem if you decide to use char const* as type for id. If you switch it to std::string, it won't be a problem.
Copy constructor of HashTable makes a shallow copy of table. This will be a problem if you decide to delete the table in the destructor of HashTable. If you don't delete table in the destructor of HashTable, you have a memory leak.
In deepcopy, you are not checking whether array[i] is NULL before dereferencing it. This has already been pointed out by #alphashooter. Additionally, you are creating a deep copy in a local variable of the function,autre. The deep copy is not visible outside the function unless you return autre from it.
EDIT
Since you are not allowed, to use std::string, you will have to allocate memory for the char const* in the default constructor as well as the copy constructor. If your platform has the non-standard function strdup and you are allowed to use it, you can change the default constructor to:
Person(const char * id,int nb)
{
this->id=strdup(id);
this->next=NULL;
}
You need to make a similar change to the copy constructor.
If you don't have strdup or you are not allowed to use it, you can define it. It's a very simple function to write.
char* strdup(char const* in)
{
char* ret = new char[strlen(in)+1];
strcpy(ret, in);
return ret;
}
Related
I've got some training example of selt-made vector, it's not template for simplicity:
class UglyStringsVector {
public:
UglyStringsVector() = default;
explicit UglyStringsVector(size_t size);
UglyStringsVector(const UglyStringsVector&);
~UglyStringsVector();
std::string &operator[](size_t index);
std::string *begin();
std::string *end();
const std::string *begin() const;
const std::string *end() const;
size_t Size() const;
size_t Capacity() const;
void PushBack(std::string value);
void operator=(const UglyStringsVector &other);
private:
std::string *data = nullptr;
size_t size = 0;
size_t capacity = 0;
void ExpandIfNeeded();
};
Assignment operator is not implemented correctly:
UglyStringsVector& UglyStringsVector::operator=(const UglyStringsVector &other) {
delete[] data;
data = new std::string[other.capacity];
size = other.size;
capacity = other.capacity;
copy(other.begin(), other.end(), begin());
return *this;
}
As we see here, when this == &other (I don't check this condition on purpose of question), it deletes its own memory (of course this is wrong), and then reallocates new strings on the same place (data = new std::string[other.capacity];), strings are not uninitialized, because default constructor was called during operator new[], and then copy strings to themselves (copy(other.begin(), other.end(), begin());).
Lets imagine than losing memory is not problem :-)
Something whispers to me that copying memory to itself is undefined behavior, but I'm not sure.
The question: is there any undefined behavior?
Assuming that data is a valid pointer or nullptr, then actually there's no UB at all.
With new std::string[other.capacity] you create an "array" of default-initialized std::string objects. A default-initialized (default-constructed basically) std::string is a valid but empty string.
You then copy this array of empty strings to itself, which is fine.
Regarding the self-copying, it's similar to
int a = 0;
a = a;
which is weird but fine.
There is no any undefined behavior. You simply delete a memory part which is pointed by the data pointer, and then you re-allocate a new array and assign it to data.
#include <iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *sptr;
public:
String()
{
}
String(char str[])
{
sptr = new char[strlen(str) + 1];
strcpy( sptr, str );
}
String(const String& source)
{
sptr = new char[strlen(source.sptr) + 1];
strcpy( sptr, source.sptr);
}
~String()
{
delete sptr;
}
String operator=( const String& other )
{
if(&other!=NULL)
{
String tmp( other );
sptr = new char[strlen(tmp.sptr) + 1];
strcpy( sptr, tmp.sptr);
}
return *this;
}
void display()
{
for( char const* p = sptr; *p != '\0'; ++ p) {
std::cout << *p;
}
cout<<endl;
}
};
int main()
{
String a1;
String a2("Hello ");
String a3(a2);
a2.display();
a3.display();
//a2.change("Java ");
a2.display();
a3.display();
}
The Output pf the program is
Hello Hello Hello Hello.
But I am undone to do the following changes in my code... i.e.. In Main () I created the following objects
String a1;
String a2("Hello ");
String a3(a2);
a2.display();
a3.display();
//a2.change("Java ");
a2.display();
a3.display();
I want to change the the object a2(commented) with Java and desired the output as
Hello Hello Java Hello
through deep copy of the Data members through this pointers.
How can I change the Hello string to java
Your String class cannot be used to build a program upon, due to the faults within the String class. You must fix those errors first before even thinking about using String as a string class.
Therefore this answer addresses how to fix String first -- from there, you should now be able to write the program knowing you're not working on a faulty foundation.
Error 1: Default construction
You failed to initialize sptr when a String is default constructed. Thus when ~String() is executed, the call to delete will attempt to delete an uninitialized pointer.
Simple 1 line main functions such as this one:
int main()
{
String s;
} // <-- destructor of s may crash
will show undefined behavior and may crash, as shown by this example.
To fix this:
class String
{
public:
String() : sptr(nullptr) {}
//...
};
Now when delete [] is issued on sptr, no harm will occur since it is perfectly valid to delete [] a nullptr (it becomes basically a no-op).
Error 2: Faulty assignment operator
Your String::operator= fails to deallocate the previous memory that was allocated. In addition, the check for NULL is incorrect, since &other will always be true.
I am assuming this is the check for self-assignment, but it is not written correctly. The check for self-assignment is done by comparing the address of the current object (this) with the address of the object being passed in:
if (this != &other)
Once you have that, then you could have written the rest of the function this way:
if(this != &other)
{
String tmp( other );
std::swap(tmp.sptr, sptr);
}
return *this;
All this function does is copy other to a temporary String called tmp, swap out the sptr with the tmp.sptr and let tmp die off with the old data. This is called the copy / swap idiom, where you're just swapping out the contents of the current object with the contents of the temporary object, and letting the temporary object die off with the old contents.
Note that the check for self-assignment isn't necessary when using copy / swap, but there is no harm done in making the check (could even be a small optimization done when the check for self-assignment is present).
Edit: The other issue is that operator= should return a reference to the current object, not a brand new String object. The signature should be:
String& operator=( const String& other ) // <-- note the return type is String&
{
//... code
//...
return *this;
}
Error 3: Wrong form of delete
Since you allocated using new [], you should be using delete [], not just delete in the destructor:
~String()
{
delete [] sptr;
}
Given all of these changes, the following code no longer has issues, as shown by the Live Example here.
Now that you have a working String class, then you can build your application from there.
As to your program, you can easily change your string by using the assignment operator. There is no need for a change() function:
String a2("Hello ");
a2.display();
a2 = "Java ";
a2.display();
Since String::operator=(const String&) is no longer faulty with the changes above, assignment can now be done without issues.
I have tried to read lots of solutions to memory leaks occur in copy constructor, but i still didnt understans how to solve it.
For example i have a "Person" class that has those mambers and functions (header files):
#include "Object.h"
#include <string.h>
#include <iostream>
using namespace std;
class Person: public Object
{
private:
char * p_name;
int length;
public:
virtual Object * copy() const;
virtual void print(ostream & os) const;
Person(char * name);
Person(const Person & per);
~Person();
};
In this program Im trying to enter "Objects" to a Vector while Person and Vector inherit from Object.
In both of the copy const i have memory leak problems (the program is working excellent).
For example in this code im getting a memory leaks of all of those 5 char arrays. I have more problems also in Vector memory leaks, but lets start in this simple code in the main (that occurs 5 memory leaks of the char array):
int main ()
{
const int SIZE = 5;
Person* persons[SIZE];
int i;
// preparation of name array
for (i = 0; i<SIZE; i++) {
char* tmp = new char[10];
sprintf(tmp, "P-%d", i);
persons[i] = new Person(tmp);
}
for (i = 0; i < SIZE; i++)
delete persons[i];
return 0;
}
Person class is:
#include "Person.h"
using namespace std;
Object * Person::copy() const
{
Person * p = new Person(*this);
return p;
}
void Person::print(ostream & os) const
{
for (int i = 0; i < this->length-1; i++)
{
os << this->p_name[i];
}
}
Person::Person(char * name)
{
delete this->p_name;
this->length = strlen(name)+1;
p_name = new char[length];
strncpy(p_name, name, length);
}
Person::Person(const Person & per)
{
delete[] this->p_name;
this->length = strlen(per.p_name) + 1;
this->p_name = new char[this->length];
strncpy(this->p_name, per.p_name, this->length);
}
Person::~Person()
{
delete[] this->p_name;
}
I would be thankful for your help!!
In main(), the tmp char array is not deleted, and that's the first memory leakage I'm seeing.
In the Person(char * name) constructor you call a delete on
Person::Person(char * name)
{
delete this->p_name;
the p_name is not allocated, so the behavior is undefined. And p_name is an array, so delete[] should be used.
if you use the std::string class, you can at least avoid the confusion between delete and delete[]
You don't have just a memory leak. You have a full-fledged, memory-corrupting, undefined behavior:
Person::Person(char * name)
{
delete this->p_name;
This is your constructor. The p_name class member does not appear to be initialized in any way. And the first thing you do, is you try to delete it.
So, whatever random value p_name will contain, as a result of the most recent set of cosmic rays striking the RAM capacitors that hold its initial value, the first thing the constructor does is try to free the pointed-to memory.
Before you can worry about any alleged leaks from your copy constructor, you need to fix a bunch of problems, like this, elsewhere.
in main(), where you prepare your c you allocate 5 char buffers, that're never freed
also (not speaking of std::string which would help you with your string stuff) , get rid of the unneeded "this->"s:
Person::Person(const Person & per)
{
delete[] this->p_name;
this->length = strlen(per.p_name) + 1;
this->p_name = new char[this->length];
strncpy(this->p_name, per.p_name, this->length);
}
could look like this:
Person::Person(const Person & per)
{
delete[] p_name;
length = strlen(per.p_name) + 1;
p_name = new char[length];
strncpy(p_name, per.p_name, length);
}
There are some problems with delete.
In your main, in the first while you have to delete temp; in the second well just don't use it, use delete [] persons.
In your copy constructor don't use delete this->p_name, that's not correct.
At first you have to set the pointer to null so p_name=NULL, and then use setters and getters, same for your value constructor.
And in your destructor, test if p_name exists before deleting it.
I tried to search same questions, but not one helped me. When I run program I get the "A Buffer Overrun has occurred..." error.
Constr:
Player(char* n)
{
length = strlen(n);
name = new char[length+1];
for(unsigned int i(0); i < length; i++)
name[i] = n[i];
name[length] = '\0';
}
Destr:
~Player(void)
{
delete [] name;
}
I've NULL terminated string and don't get out of bounds, what is problem?
There's no obvious error in the code you've posted, but trying to manage dynamic memory by juggling raw pointers will almost inevitably lead to errors like this.
Perhaps you haven't correctly implemented or deleted the copy constructor and copy-assignment operator, per the Rule of Three. In that case, copying a Player object will give two objects with pointers to the same array; both of them will try to delete that array, giving undefined behaviour.
The simplest solution is to manage your string with a class designed for managing strings. Change the type of name to std::string, and then the constructor can simply be something like
explicit Player(std::string const & n) : name(n) {}
and there's no need to declare a destructor (or move/copy constructor/assignment operators) at all.
So... a solution by using an std::string has been provided, but let me give another solution, keeping your member variables intact.
The problem is this. Suppose you have this code somewhere:
Player p1("Bob"); // Okay
Player p2("Annie"); // Okay
p2 = p1; // Oops! (1)
Player p3(p1); // Oops! (2)
At (1), the method Player& Player::operator=(const Player&) is called. Since you didn't provide one, the compiler generates one for you. When it does, it simply assumes that it may copy over all member variables. In this case, it copies over Player::name and Player::length. So, we have p1.name == p2.name. Now when the destructor of p2 is called, the allocated memory pointed to by p2.name is deleted. Then when the destructor of p1 is called, the same memory will be deleted (since p1.name == p2.name)! That's illegal.
To fix this, you can write an assignment operator yourself.
Player& Player::operator = (const Player& other)
{
// Are we the same object?
if (this == &other) return *this;
// Delete the memory. So call the destructor.
this->~Player();
// Make room for the new name.
length = other.length;
name = new char[length + 1];
// Copy it over.
for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
name[length] = '\0';
// All done!
return *this;
}
At (2), the same problem occurs. You do not have a copy constructor, so the compiler generates one for you. It will also assume that it may copy over all the member variables, so when the destructors get called, they'll try to delete the same memory again. To fix this, also write a copy constructor:
Player::Player(const Player& other)
{
if (this == &other) return;
length = other.length;
name = new char[length + 1];
for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
}
At the end of the day you should use an std::string though.
I have a class containing a member pointer which is dynamically allocated in its constructor as follows:
class Record {
public:
Record(unsigned short numBytes, char* bufRecord);
~Record();
unsigned short size() {return m_numBytes;}
private:
unsigned short m_numBytes;
char* m_bufRecord;
};
Record::Record(unsigned short numBytes, char* bufRecord) {
m_numBytes = numBytes;
m_bufRecord = new char[numBytes];
for(unsigned short i=0; i<numBytes; i++)
m_bufRecord[i] = bufRecord[i];
}
Record::~Record() {
delete m_bufRecord;
}
It basically copies the input buffer into the dynamically allocated member buffer. I proceed to use this class as follows, in the constructor of another class:
class File {
public:
File(const char* fileName);
~File();
unsigned int numRecords() {return m_records.size();}
Record getRecord(unsigned int numRecord) {return m_gdsRecords[numRecord];}
private:
std::ifstream m_file;
std::vector<Record> m_records;
};
File::File(const char* fileName) : m_file(fileName, ios::in | ios::binary) {
while(!m_file.eof()) {
char bufNumBytes[2];
char* bufRecord;
unsigned short numBytes;
m_file.read(bufNumBytes, 2);
numBytes = (bufNumBytes[0] << 8) + bufNumBytes[1] - 2;
bufRecord = new char[numBytes];
m_file.read(bufRecord, numBytes);
Record record(numBytes, bufRecord);
m_records.push_back(record);
delete bufRecord;
}
}
However, when I instantiate this class, I get the following error, which seems to state that I'm double-freeing the m_bufRecord:
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001cb3280 ***
I'm guessing the problem lies with the insertion of a class containing a pointer to the vector element, and the destructor being called twice on the same pointer but I'm not sure how this happens. What am I doing wrong here?
This is a case of the Rule of three. If your class needs to free resources in the destructor, it generally needs to declare a copy constructor (and copy assignment operator), to either copy the owned resource, manage shared ownership or prevent being copied.
Record getRecord(unsigned int numRecord) {return m_gdsRecords[numRecord];}
This function returns a copy of the Record. So now you have two Records containing the same m_bufRecord pointer. Running the destructors on these Records will try to delete the same pointer value twice.