Can someone explain to me why...
DataStructure<MyClass> ds;
cin >> size;
ds = DataStructure<MyClass>(size);
causes my program to crash, but...
cin >> size;
DataStructure<MyClass> ds = DataStructure<MyClass>(size);
does not?
I think it has something to do with my program using the default constructor and followed by an attempt to use the implicit copy constructor but I am not sure.
To give more context, I'm creating a hash table class and in the default constructor, I initialize the array with data to nullptr and in the constructor with the size argument, I create the array with the data to new T * [size] and set each element to nullptr.
Constructor without any parameters:
this->data = nullptr;
vs.
Constructor with size parameter:
this->data= new T * [size];
for(int i = 0; i< size; i++)
{
data[i] = nullptr;
}
You will need to declare a copy constructor. If you do not have a copy constructor then all members will be copied. In your case data will point to the data reserved in the second class. Next, this data will be destroyed together with the class and points to nothing. That will most likely cause your program to crash.
Your copy constructor should do deep copy, something like this:
DataStructure(const DataStructure &rhs)
{
if (this->data) delete[] data;
this->data = new T*[rhs.GetSize()];
for (int i=0; i<rhs.GetSize(); i++)
{
this->data[i] = rhs.data[i];
}
return *this;
}
Related
I am writing code for a backtracking approach to a Traveling Salesman type of problem. So at each point i will recurse for rest of the un-visited points.
I could not use any library/functions other than cout, cin, new and delete (so no vector). So for the problem i want to keep a track of what all points i have visited till now. I am using a dynamic boolean array for this. So i want to pass the dynamic array to a function as value to keep track of this.
This is what i have tried till now.
I tried to wrap the array in a struct, but the memory dealocation (delete) is giving error (Segmentation fault)
typedef struct Barray{
bool* a;
int size;
Barray(int size) { a = new bool[size]; this->size = size; }
Barray(const Barray& in) {
if(a) delete[] a; // error
a = new bool[in.size];
this->size = in.size;
for (int i = 0; i < in.size; i++)
a[i] = in.a[i];
}
~Barray() { delete[] a; } // error
}barray;
This is my recursive function call
void find_mindist(barray visited, int dist_now, int cur_p) {
if (base condition)
{return ;}
for (int i = 0; i < n; i++) {
if (visited.a[i]) continue;
barray tdist = visited;
tdist.a[i] = true;
int ndist = dist_now + dist(points[cur_p], points[i]);
find_mindist(tdist, ndist, i);
}
return ;
}
So my questions are -
how can i pass a dynamic array to a function as value?
Why is the delete above giving error?
First of all, the recommended approach for a local visited information is not the endless copying of the whole visited collection, but a mark->recurse->unmark approach. So whatever you do, please keep a single boolean array for the visited information and update its content to your needs.
The other problems occur because you try to delete an uninitialized pointer in the copy constructor. Also, the assignment operator should be overloaded as well to avoid unpleasent surprises. But non of this really matters if you don't copy your visited information anymore.
The problem this is a copy constructor. As such, on entry, a is uninitialized (so contains garbage), so the delete is invalid.
Barray(const Barray& in) {
if(a) delete[] a; // error
a = new bool[in.size];
this->size = in.size;
for (int i = 0; i < in.size; i++)
a[i] = in.a[i];
}
Just remove the delete line. Also, prefer to initialize members, rather
than assign them, so:
Barray(const Barray& in)
: a(new bool[in.size])
, size(in.size) {
for (int i = 0; i < in.size; i++)
a[i] = in.a[i];
}
Also, remember the Rule of Three. You need an copy assignment operator. The simplest is:
Barry& operator=(const Barray& in) = delete;
which just forces a compilation error if you try to use it! Better is:
Barry& operator=(const Barray in) { // **NOTE** pass by value!
std::swap(this.a, in.a);
std::swap(this.size, in.size);
}
This version provides the strong exception guarantee. You aren't allowed to use std::swap, so you'll either have to write your own, or write it out by hand (you choose).
Finally, if you ever find yourself returning a Barray, you should write a move constructor:
Barray(Barray &&in)
: a(in.a)
, size(in.size) {
in.a = nullptr;
}
This can save a lot of copying!
I have want to send a struct to json->setInformation but my program crashes when i try to copy the array which is inside the struct. The rest of the data is okay its just the array which makes the crash occur.
info = data->getInformation();
json->setInformation(info);
getInformation returns a struct which i can read in main.cpp
when i try to send this struct to setInformation it crashes...
information.h which holds my struct
struct information{
String location;
String protocol;
uint8_t groupID;
uint8_t* data;
information& operator=(const struct information& that){
location = that.location;
protocol = that.protocol;
groupID = that.groupID;
for (int i = 0; i < 9; ++i){
data[i] = that.data[i];
}
return *this;
}
};
json.cpp
void JSON::setInformation(information data){
info->location = data.location;
info->protocol = data.protocol;
info->groupID = data.groupID;
// for (int i = 0; i < 9; ++i){
// info->data[i] = data.data[i];
// }
// Serial.print("after JSON: ");
// Serial.println(info->data[0]);
}
this code works fine but when i uncomment the for lop which should copy the array it crashes
Did you allocate memory for your uint8_t data* parameter before using it ?
Then remember to deallocate memory when you don't need it anymore, thus avoiding memory leaks.
Your object is passed by copy to the function, but you have no copy constructor.
Default copy constructor will not copy you raw pointer correctly. So either you declare and implement a copy constructor, either you replace your raw pointer (uint8_t*) by a vector (std::vector<uint8_t>) which is safely copyiable (then copying the object will become a valid operation).
Moreover, we can't see who's allocating/deallocating your raw pointer, but I suspect you are missing a destructor function too.
Your code breaks the rule of three which is the minimal requirement for any class you'll declare in C++.
Suppose we have the following:
class StringClass
{
public:
...
void someProcessing( );
...
StringClass& operator=(const StringClass& rtSide);
...
private:
char *a;//Dynamic array for characters in the string
int capacity;//size of dynamic array a
int length;//Number of characters in a
};
StringClass& StringClass::operator=(const StringClass& rtSide)
{
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
return *this;
}
My question is: why does this implementation of overloading the assignment operator cause problems when we try to assign an object to itself like:
StringClass s;
s = s;
The textbook I'm reading (Absolute C++) says that after delete [] a; "The pointer s.a is then undefined. The assignment operator has corrupted the object s and this run of the program is probably ruined."
Why has the operator corrupted s? If we're reinitalizing s.a right after we delete it, why does this cause such a problem in the program that we have to redefine the function as:
StringClass& StringClass::operator=(const StringClass& rtSide)
{
if (this == &rtSide)
//if the right side is the same as the left side
{
return *this;
}
else
{
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
return *this;
}
}
If you are assigning an object to itself both a and rt.a point to the same string, so when you do delete [] a you are deleting both what a and rt.a point to; then you do reallocate it, but the data you were going to copy (on itself) in the loop has been lost in the delete.
In the loop now you will just copy whatever junk happens to be in the memory returned by new on itself.
By the way, even with the "safety net" of the self-assignment check that assignment operator isn't completely ok (for instance, it's not exception safe); the "safe" way to define the "big three" (copy constructor, assignment operator, destructor) is using the "copy and swap idiom".
If you self-assign, you free (delete) the string via the LHS argument before you copy it to the newly allocated space via the RHS argument. This is not a recipe for happiness; it is undefined behaviour and anything may happen. A crash is plausible; if you're really unlucky, it may appear to work.
Consider what the value of rtSide.a is when you're inside the broken operator=.
It's the same as this->a, the values you just clobbered. Accessing non-owned memory is undefined behavior, thus accessing this->a is undefined behavior (since you just freed it).
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i]; //Invalid when this->a == rtSide.a
//because rtSide.a is no longer owned by your program.
If you did actually want to do this, you would have to make a copy of a before deleting it:
char* ca;
if (this == &rtSide) {
ca = copy of rtSide.a or this->a;
} else {
ca = rtSide.a;
}
//Do your assigning and whatnot
if (this == &rtSide) {
delete[] ca;
}
Obviously it's much more efficient to just do nothing instead of making temporary copies of all of an instances own members. It's the same concept as doing int x = 5; int y = x; x = y;
It is because you've first deleted the pointer delete [] a;
and then later on trying to copy from the deleted location:
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i]; //rtSide has already been deleted as 'this' and '&rtSide' are same.
Remember it is the same location you are trying to copy from, which you've already deleted.
Hence, the error!
The later code you posted fixes this problem by checking for self-assignment as a separate case.
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
That's why. Think of it like this:
You delete whatever a points to, then allocate a new chunk of memory. The new chunk of memory contains garbage which becomes your new data. Do not be confused by the loop that does a[i] = rtSide.a[i]; that only copies the garbage onto itself.
Remember, this and rtSide both lead you to the same object. When you modify the object using this the object that rtSide refers to is modified.
I have a header file:
using namespace std;
class IntList{
private:
int *Intl;
int Capacity;
int Count;
public:
IntList(int capacity){
Capacity = capacity;
Count = 0;
Intl = new int[capacity];
}
~IntList(){
delete Intl;
}
//adds the integers of the specified collection to the end of the List; return false if the new Count will be greater than Capacity
bool AddRange(const IntList &items){
//int *Temp = items.;
if(items.Count > Capacity - Count){
return false;
}else{
for(int i = 0; i <items.Count; i++){
Intl[Count] = items.Intl[i];
Count++;
}
return true;
}
}
};
But I don't know why I can't return value to IntList object in there:
//creates a copy of a range of elements in the source List
IntList GetRange(int index, int count){
IntList A(count);
for(int i = 0; i < count; i++){
A.Intl[i] = Intl[index -1 +i];
}
return A;
}
I want to return value of A whose type is IntList but I meet an error on "_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) in visual studio 2010. How can I repair it?
Because int *Intl; is an object you manually manage, you'll need to implement the copy constructor for your class.
The function GetRange returns by value. The local object A gets destroyed, and its member Intl gets deleted in the destructor, so your copy (as copied by the default copy constructor) is only a shallow one, and will contain an invalid member.
EDIT: As Rob correctly pointed out, you'll also need to implement the assignment operator (you already have a destructor).
For an object which is returned by value makes a call to copy-constructor . You must make a copy-constructor and define it so that you get appropriate results. Returning by reference actually does not require call to copy constructor but should not be made for a temporary object. Also since you have a pointer type as member variable in class . It would be appropiate for you to overload the = operator. It should be define properly to avoid memory leak. Do something like this Intlist a=GetRange(index,count) . Also you should create a copy constructor for this . Your code also has a bug that it doesnot overload= operator for class Intlist .
you can write a copy constructor something like this :-
Intlist::Intlist(const Intlist& cSource)
{
capacity = cSource.capacity;
count= cSource.count;
// Intl is a pointer, so we need to deep copy it if it is non-null
if (cSource.Intl)
{
// allocate memory for our copy
Intl = new int[capacity];
// Copy the Intl into our newly allocated memory in for loop
for(i=0;i<capacity;i++)
{
// copy part
}
}
else
intl = NULL;
}
Just an e.g how you should write it.
I have these variables:
char** wordList_;
int wordListCapacity_;
int* wordCountList_;
char* fileName_;
int nUniqueWords_;
int nTotalWords_;
int nTotalCharacters_;
My copy constructor:
FileIndex::FileIndex(const FileIndex& fi)
{
fileName_ = new char[strlen(fi.fileName_) + 1];
strcpy(fileName_, fi.fileName_);
cout << "Jiasd?" << endl;
wordListCapacity_ = fi.wordListCapacity_;
nUniqueWords_ = fi.nUniqueWords_;
nTotalWords_ = fi.nTotalWords_;
nTotalCharacters_ = fi.nTotalCharacters_;
wordList_ = new char*[wordListCapacity_];
wordCountList_ = new int[wordListCapacity_];
for(int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = fi.wordList_[i];
wordCountList_[i] = fi.wordCountList_[i];
}
}
My overloaded assignment operator:
FileIndex& FileIndex::operator=(const FileIndex& fi)
{
fileName_ = new char[strlen(fi.fileName_) + 1];
strcpy(fileName_, fi.fileName_);
wordListCapacity_ = fi.wordListCapacity_;
nUniqueWords_ = fi.nUniqueWords_;
nTotalWords_ = fi.nUniqueWords_;
nTotalCharacters_ = fi.nTotalCharacters_;
wordList_ = new char*[wordListCapacity_];
wordCountList_ = new int[wordListCapacity_];
for (int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = new char[strlen(fi.wordList_[i])+1];
strcpy(wordList_[i], fi.wordList_[i]);
wordCountList_[i] = fi.wordCountList_[i];
}
return *this;
}
Whenever I create a FileIndex (called FirstIndex) and initialize the member variables with something meaningful (not NULL) I have these lines to test the copy constructor and assignment operator:
FileIndex secondIndex = firstIndex;
FileIndex thirdIndex;
secondIndex = thirdIndex; // Segmentation fault here
I get a segmentation fault with the assignment operator but I have a feeling it may be because of faulty code in the copy constructor. That being said, if there's an error in the copy constructor then there's also probably one in the assignment operator.
Thanks in advance for the help!
I think you want to use std::string and std::vector<T> for your class. Also, for the purpose of what goes wrong it would be necessary to see the default constructor and the destructor. From the look of you setup it seems that you may e.g. not have initialized some of the members in your default constructor. Also, you assignment operator has several resource leaks and will be pretty bad off if you try self assignment. In general, I'd recommend to implement assignment operators like this:
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
This leverages the work done for the copy constructor and use a swap() member which is generally very easy to do.
Check out your copy constructor.
for(int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = fi.wordList_[i];
wordCountList_[i] = fi.wordCountList_[i];
}
The problem is with wordList_[i] = fi.wordList_[i];. You are not allocating new memory and doing a strcpy here as you do in the assignment operator. Instead your new copy is actually pointing to data from the instance that it is copying from. I believe this may be what David Schwartz was alluding to.
It looks as if you may not initialize wordListCapacity_ correctly (it's hard to tell since you don't show the default ctor). Since it is an int, it can have a negative value, which can cause a segfault when you attempt wordList_ = new char*[wordListCapacity_];. There may be other problems.