I Have a doubt in the following code, there is a destructor delete line[] inside the destructor, I just want to know if there any stack over flow for this delete which can be a result of recursive call to destructor.
class Line {
public:
char *line;
Line(const char *s = 0) {
if (s) {
line = new char[strlen(s)+1];
strcpy(line, s);
} else {
line = 0;
}
}
~Line() {
delete[] line; //----------> how this delete will work?
line = 0;
}
Line &operator=(const Line &other) {
std::cout <<"go"<< endl;
delete[] line; //----------> purpose of using this delete??
line = new char[other.len()+1];
strcpy(line, other.line);
return *this;
}
int operator<=(const Line &other) {
int cmp = strcmp(line, other.line);
return cmp <= 0;
}
int len() const {
return strlen(line);
}
};
int main() {
Line array[] = {Line("abc"), Line("def"),
Line("xyz")};
Line tmp;
}
The delete inside the overloaded assignment operator is to clean the memory before assigning new memory(I have read it somewhere, correct me if I am wrong), but does this delete will call the destructor?
Please Explain
No, it will not.
This delete statement will delete the char array. The destructor of Line is only called when destroying the Line object. That is however not the case here.
The variable line and the object/class Line are different things.
The line variable is a member-variable in the Line class. So those two names seem the same but are completly different.
delete[] line; pairs the new char[strlen(s)+1]; statements in the constructor and assignment operator. Note that delete[] line; is a no-op if line is set to nullptr, which is what the else branch assignment does, although it sloppily uses 0 in place of nullptr.
Be assured the destructor is not called recursively. It's just that the destructor is used to release any allocated memory.
But using std::string line; as the class member variable, or perhaps even the entire class itself would be far, far easier. There are some subtle bugs in your code - self assignment being one of them, and the copy constructor is missing. Let the C++ Standard Library take care of all this for you. In short you could write
int main() {
std::string array[] = {"abc", "def", "xyz"};
std::string tmp;
}
The argument to delete[] is a char*, ie there is no destructor called (and also there is no recursive calling of the destructor).
If you had a destructor like this:
~Line() { delete this; } // DONT DO THIS !!! (also for other reasons it is not OK at all)
this would attempt to call itself recursively, but your code looks fine.
In the assignment operator
line = new char[other.len()+1];
will allocate new memory and assign a pointer (pointing to this memory) to line. This will cause that you have no handle to the old memory anymore and to avoid a leak you need to delete it before.
C++ by default takes care of delete[] for char*, therefore you do not need to do anything.
Related
First of all, I was not using C++ for a lot time and it is possible that this question is very easy and it does not deserve to be posted here. Anyway, I did not found any solution here or in other source.
My problem consists in following. Let's assume we have class A:
Class A
{
char* string;
public:
char*& getString(){ return string; }
~A()
{
if (string)
delete[] string;
}
};
I cannot modify this class(it is just a sample of the real class).
I want to set field string to a value:
int main()
{
A a;
a.getString() = new char[3];
a.getString() = "Hi\0";
return 0;
}
This code cause Debug Assertion Fail when destructor ~A() is called. What I'm doing wrong here?
I really will appreciate any suggestion about what I'm doing wrong.
EDIT:
It seems, that assignment operator here is important. Actually, I'm doing such an assignment:
int main()
{
A a;
char name[256];
std::cin.getline(name, 256);
a.getString() = new char[strlen(name)];
//actual version
strcpy_s(a.getString(), strlen(name), name);
//a.getString() = "Hi\0";
return 0;
}
This code cause Debug Assertion Fail when destructor ~A() is called. What I'm doing wrong here?
a.getString() = "Hi\0";
After this line, a.string points to a string literal. The destructor will then call delete[] on the pointer. Deleting a string literal has undefined behaviour. Also, the previously allocated dynamic array is leaked, since the pointer was overwritten.
The solution is to remove the quoted line.
I am trying to understand the concept of copy constructor. I used this example:
#include <iostream>
using namespace std;
class Line
{
public:
int GetLength();
Line(int len);
Line(const Line &obj);
~Line();
private:
int *ptr;
};
Line::Line(int len)
{
ptr = new int;
*ptr = len;
};
Line::Line(const Line &obj)
{
cout << "Copying... " << endl;
ptr = new int;
*ptr = *obj.ptr;
};
Line::~Line()
{
delete ptr;
};
int Line::GetLength()
{
return *ptr;
}
int main()
{
Line line1 = Line(4);
cout << line1.GetLength() << endl;
Line line2 = line1;
line1.~Line();
cout << line2.GetLength() << endl;
return 0;
}
The question is, why do I get runtime error here? If I defined a copy constructor which allocates memory for the new ptr, and assigned the line1 to line2, doesn't that mean that those two are separate objects? By destructing line1, I obviously mess up line2 as well, or am I using the destructor call wrong?
You called the destructor in this statement
line1.~Line();
which deleted the memory allocated for ptr
Line::~Line()
{
delete ptr;
};
However the object line1 is alive because it has automatic storage duration. So after exiting main the destructor for the object will be called one more and as result it will try to delete the memory pointed to by ptr that was already deleted explicitly.
line1.~Line();
Manually calling a destructor is useful only if you use placement new.
I don't understand what gave you the idea to call the destructor manually in this program. You don't really want to know such a low-level memory-management mechanism when you are still new to the language, but for the sake of completeness, it would work like this:
int main()
{
// provide static memory with enough space for one Line object:
char buffer[sizeof(Line)];
// create a Line object and place it into buffer:
Line* line1 = new (buffer) Line(4);
cout << line1->GetLength() << endl;
Line line2 = *line1;
// manually call the destructor:
line1->~Line();
cout << line2.GetLength() << endl;
// - no delete necessary because buffer disappears automatically
// - no automatic destructor call
return 0;
}
Your code, however, results in an attempt to call the destructor of line1 twice. First manually, then automatically when the object's scope ends, i.e. at the end of main. This is undefined behaviour. See Does explicitly calling destructor result in Undefined Behavior here?
The question is, why do I get runtime error here?
Because undefined behaviour means that your program can do or not do anything. The runtime error is not guaranteed, nor are you guaranteed any deterministic behaviour at all.
Please refer below program before answering question. Explained the code in comments.
So my question here is in assignment operator overloading how to handle the case where new() failed to allocate memory.
For example Obj1 is holding string "GeeksQuiz". Assigning Obj2 to Obj1. During assigning (in assignment operator overload function) first we free Obj1 and then recreate Obj1 with Obj2 values. So in the case where new fails to allocate memory how to retain old Obj1 values? Since we freed Obj1 values in starting of function.
All that I want is to have the old values for Obj1 when the assigning operation fails.
Please help me in this. I want perfect code, without any memory leaks covering all scenarios. Thanks in Advance
#include<iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *string_data;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] string_data; }// destructor
void print() { cout << string_data << endl; } // Function to print string
String& operator = (const String &); // assignment operator overload
};
String::String(const char *str) // Constructor
{
size = strlen(str);
string_data = new char[size+1];
if (string_data != NULL)
strcpy(string_data, str);
else
cout<<"compiler failed to allocate new memory";
}
String& String::operator = (const String &str) // assignment operator overload
{
if(this != &str)
{
delete [] string_data; // Deleting old data and assigning new data below
size = str.size;
string_data = new char[size+1];
if(string_data != NULL) // This condition is for cheking new memory is success
strcpy(string_data, str.string_data);
else
cout<<"compiler failed to allocate new memory"; // My quetsion comes in this scenario...
}
return *this;
}
int main()
{
String Obj1("GeeksQuiz");
String Obj2("stackoverflow");
Obj1.print(); // Printing Before assigment
Obj2.print();
Obj1 = Obj2; // Assignment is done.
Obj1.print(); // Printing After assigment
Obj2.print();
return 0;
}
First of all, implementing a robust string is difficult, unless you want to do it for learning purposes always use std::string.
Then take into account that operator new always returns non-null pointers (unless you are also implementing a non standard custom new operator), instead it throws a std::bad_alloc exception if it fails to allocate the data. If you want to handle the allocation fail case you need to add a try-catch block
char *data = NULL;
try {
data = new char[str.size + 1];
} catch (std::bad_alloc &e) {
std::cout << "Allocation failed: " << e.what() << std::endl;
throw; // <- You probably want to rethrow the exception.
}
strcpy(data, str.string_data);
delete [] string_data;
string_data = data;
size = str.size;
The important part is to leave your class in a consistent state when the exception is thrown, that's why you must first allocate the new data and then if it succeeds, delete the old data. However bad_alloc exceptions are rarely handled at class level, commonly you let the exception be thrown (that's why I rethrow in the code sample) and let the client code handle that.
If you really want your code to be exception proof I would advice the use of smart pointers, and as already said, in this case use std::string.
Temporary or dummy variables.
Allocate new memory, assign pointer to a temporary variable. If it succeeds then free the old memory and reassign that pointer variable.
Pseudo-ish code:
char *temp = new char[new_size];
std::copy(new_data, new_data + new_size, temp);
delete [] old_data;
old_data = temp;
old_size = new_size;
1st allocate memory in a temporary variable, if it's successful then only delete old value.
This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 8 years ago.
I have problem with the following class. I think the problem is with string array, cause I made two other classes and the problem was the same. When I run the program it throws "double free or corruption", but I do not think any double corruption is possible. The problem is same with input string as reference or as common argument in Add method.
class WareH
{
public:
WareH(void)
{
first = true;
rows = 1;
inLine = 0;
cnt = 0;
max = 2;
cL = 0;
strs = new string[max];
}
~WareH(void)
{
delete [] strs;
}
bool Add(string& str, int ending)
{
if (first)
inLine++;
else
cL++;
if (ending == 0)
{
if (first)
first = false;
if (cL != inLine)
return false;
rows++;
}
strs[cnt++] = str;
Bigger();
return true;
}
void Bigger(void)
{
if(max == cnt)
{
max *= 2;
string* tmp = new string[max];
for (int i = 0; i < cnt; i++)
tmp[i] = strs[i];
delete [] strs;
strs = tmp;
}
}
friend ofstream& operator<<(ofstream& of,WareH war)
{
for (int a = 0; a < war.cnt; a++)
of << war.strs[a] << endl;
return of;
}
private:
bool first;
int rows, inLine, cnt, max, cL;
string* strs;
};
When a class manages resources, and releases them in its destructor, you must consider the Rule of Three to make sure that copying an object will not result in two objects managing the same resource.
That is what is happening here: the default copy constructor and copy-assignment operator will copy the pointer, giving you two objects which will both try to delete the same array on destruction. Solutions are:
Delete the copy constructor and copy-assignment operator to prevent copying; or
Implement them to copy the strings into a new array, not just the pointer; or
Use std::vector rather than messing around managing memory allocation yourself.
When I run the program it throws "double free or corruption", but I do not think any double corruption is possible.
Educated guess here:
The problem is not in the code you've shown, but in the client code. Here's what I think happens:
you wrote client code that instantiates (or assigns or returns by value or stores in a std container) WareH instances, and since you do not define a copy constructor and assignment operator (see "The Big Three"), they end up copying the values from your source objects. When the first of these instances (that are assigned to each other) are deleted, they delete the strs pointer.
When the second instance is deleted, they delete the same strs pointers that were deleted before (because the default copy constructors and assignment operators do not duplicate the allocated memory but just copy the pointers).
Solutions (if that is indeed, the problem):
working (and bad) solution: explicitly define copy construction and assignment operator for your class.
working (and good) solution: implement your strs as a std::vector<std::string> instead of std::string* and cnt.
I am attempting to dynamically allocate memory to the heap and then delete the allocated memory. Below is the code that is giving me a hard time:
// String.cpp
#include "String.h"
String::String() {}
String::String(char* source)
{
this->Size = this->GetSize(source);
this->CharArray = new char[this->Size + 1];
int i = 0;
for (; i < this->Size; i++) this->CharArray[i] = source[i];
this->CharArray[i] = '\0';
}
int String::GetSize(const char * source)
{
int i = 0;
for (; source[i] != '\0'; i++);
return i;
}
String::~String()
{
delete[] this->CharArray;
}
Here is the error I get when the compiler tries to delete the CharArray:
0xC0000005: Access violation reading location 0xccccccc0.
And here is the last call on the stack:
msvcr100d.dll!operator delete(void * pUserData) Line 52 + 0x3 bytes C++
I am fairly certain the error exists within this piece of code but will provide you with any other information needed. Oh yeah, using VS 2010 for XP.
Edit: Heres my String.h
// String.h - string class
#pragma once
#define NOT_FOUND -1
class String
{
public:
String();
String(char* source);
static int GetSize(const char * source);
int Find(const char* aChar, int startPosition = 0);
~String();
private:
char* CharArray;
int Size;
};
Change your default ctor; given the error you're getting, the delete call is trying to delete a pointer that has never been initialized.
String::String() : Size(0), CharArray(NULL) {}
Also, beware of the "copy constructor". You might want to make it private just to be sure you're not triggering it implicitly. (It doesn't need to be implemented if you don't intend to call it, just stick the function prototype into your class definition.) Might as well similarly "disable" the assignment operator.
class String
{
// other stuff
private:
String(String&);
String& operator=(String&);
};
This addition fulfills the "Rule of Three," which says that if any class needs a destructor, a copy constructor, or an assignment operator, it probably needs all three.
Edit: see http://en.wikipedia.org/wiki/Rule_of_three_%28C%2B%2B_programming%29
String::String(): CharArray( 0 ) {}
You're not initializing CharArray in every constructor, so in some cases you're deleting an uninitialized pointer.
You have multiple constructors, but only 1 of them calls new. Your destructor always calls delete so there is your error.
I think #dash-tom-bang is correct. You're probably copying String and then deleting its data twice. I'll keep my old answers here for reference, though.
You're going to need to post the code that uses String, but I can notice a few problems here:
What if source is NULL in the constructor? Immediately you have a Null Pointer Exception. What's worse, if you get this exception, the destructor will attempt to delete memory that was never allocated. This could cause the error described above if you're using try...catch.
GetSize should not be a member function of String because it doesn't use any member variables. At the very least it should be static.