class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
//String(const String&); // copy constructor --> i get double free error while running without this
void print() { cout << s << endl; } // Function to print string
void change(const char *); // Function to change
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
void String::change(const char *str)
{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
/*String::String(const String& old_str)
{
size = old_str.size;
s = new char[size+1];
strcpy(s, old_str.s);
}*/
int main()
{
String str1("Hello");
String str2 = str1;
str1.print(); // printed
str2.print();
str2.change("Helloworld");
str1.print(); // not printed
str2.print();// printed
return 0;
}
http://ideone.com/xJtoTf
I am getting a double free error and the print for str1 is not being printed the second time (see comments in the code above)...
Is it because when i am not using copy constructor here, instead of default copy constructor being invoked assignment operator is used and both str2 and str1 are pointing to same location?
String str2 = str1; calls the copy constructor, not the assignment operator.
As you don't provide the copy constructor, the compiler will supply one automatically. All that will do is copy the member data.
So both str2 and str1 will share the same character buffer. Boom!
Related
I am creating a class that works similar to the string class. I am in a starting stage doing some experiments.
Assume all header files are added
class String
{
public:
int len;
char *str = NULL;
// Default constructor
String()
{
len = 0;
str = new char[1];
str[0] = '\0';
printf("New memory: %p\n", str);
}
// Copy constructor
String(const char *ch)
{
len = strlen(ch);
if (str != NULL)
{
printf("Old memory: %p\n", str);
delete str;
printf("Deleted...\n");
}
str = new char[len + 1];
printf("New memory: %p\n", str);
for (int i = 0; i <= len; i++)
str[i] = ch[i];
}
};
int main()
{
String msg;
std::cout << msg.len << std::endl;
std::cout << msg.str << std::endl;
msg = "Hello";
std::cout << msg.len << std::endl;
std::cout << msg.str << std::endl;
}
When I create a String object 1 byte of memory is allocated to store string and initialises with \0 on line String msg;
When copy constructor is called on line msg = "Hello"; it first checks for any previous allocation of memory and to avoid memory wastage it will delete that and recreates a new memory block.
The issue is when copy constructor is called line if (str != NULL) is not getting executed. It should execute because str is allocated a byte of memory earlier in default constructor and it is not NULL.
What may be the thing that I missed out?
Your code does this:
Default-construct a String "A"
Convert-construct a temporary String "B" from the expression "Hello"
Copy-assign "B" to "A"
The assignment does not invoke the constructor on "A". You only get one construction per object.
Unless you specify otherwise (with delegating constructors), your default constructor is not invoked before the "copy constructor" (or a conversion constructor like yours) is run.
So this:
It should execute because str is allocated a byte of memory earlier in default constructor and it is not NULL.
is wrong.
String(const char *ch) needs to allocate.
Also, because you're performing assignment, you're going to need to implement a copy assignment operator to make that safe.
For some reason, whenever it reassigns the string as a different const char*, it has an error. After it creates a new buffer on a string with a previous buffer, when it exists the constructor the buffer changes value for no reason. I am using heap memory, so it shouldn't be affected by scope.
#include <iostream>
class String {
private:
char* buffer;
unsigned int size;
public:
String(const char* string) {
size = strlen(string);
buffer = new char[size + 1];
memcpy(buffer, string, size + 1);
buffer[size] = 0;
}
String(const String& other) {
memcpy(this, new String(other.buffer), sizeof(String));
}
~String() {
delete buffer;
}
friend std::ostream& operator<<(std::ostream& stream, const String& str);
String operator+(const String& other) {
unsigned int nSize = this->size + other.size;
char* nBuffer = new char[nSize + 1];
memcpy(nBuffer, this->buffer, nSize + 1);
strcat(nBuffer, other.buffer);
nBuffer[nSize] = 0;
return String(nBuffer);
}
void operator+=(const String& other) {
unsigned int nSize = this->size + other.size;
char* nBuffer = new char[nSize + 1];
memcpy(nBuffer, this->buffer, nSize + 1);
strcat(nBuffer, other.buffer);
nBuffer[nSize] = 0;
this->~String();
memcpy(this, new String(nBuffer), sizeof(String));
}
};
std::ostream& operator<<(std::ostream& stream, const String& str) {
stream << str.buffer;
return stream;
}
int main() {
String test1 = "Hello";
test1 = "there";
std::cout << test1 << std::endl;
}
test1 = "there" is the same as test1 = String("there") because the string "there" has to be converted to a String. The default assignment operator works by assigning all the members on the right hand side to the members on the left hand side. It does a shallow copy of all the members, so your strings are not actually duplicated, but rather both buffer pointers in test1 and String("there") point to the same memory location that holds the string "there".
At the end of the assignment the String("there") object has to be destroyed, so it calls the destructor which deletes the memory that its buffer is pointing to. At the end of the main() method the same happens with test1, only when it gets there it finds that the memory has already been deleted, causing a runtime error.
When dealing with raw pointers as class members, in addition to making your own copy-constructor you also have to make your own copy-assignment operator to delete the already held memory and allocate new memory. A simply one may look like this:
String& operator=(String const& other) {
delete[] buffer;
buffer = new char[other.size + 1];
strcpy(buffer, other.buffer);
size = other.size;
return *this;
}
Now assigning into test1 will allocate an entirely seperate memory space to hold the string.
I've been attempting to troubleshoot this code with no avail. It crashed just as I call the copy constructor. In other attempts at calling the copy constructor the output of the new object has been random characters.
I have a feeling that my lack of understanding of memory management carries over to overloading the = operator too.
Here is my copy constructor....
MyString::MyString(const MyString& obj)
{
delete [] str;
int temp=obj.len;
len = temp;
str = new char[len];
for (int i = 0; i < len; i++)
{
char temp=obj.str[i];
str[i] = temp;
}
}
Here is my overloaded = ....
MyString& MyString::operator=(const MyString& obj)
{
delete [] str;
int temp = obj.len;
len = temp;
str = new char[len];
for ( int i = 0; i<=len; i++)
{
char temp = obj.str[i];
str[i] = temp;
}
str[len] = '\0';
return *this;
}
in the main method I call the copy constructor like so...
MyString stt3(str2); //str2 already exists.
Can anyone suggest where I'm going wrong?
The line
delete [] str;
in the copy constructor is not right. str is not initialized before that line. Removing that line should help, if not remove all the problems.
Also, you probably need to allocate one more object than len and null terminate str.
str = new char[len+1]; // Need +1
...
str[len] '\0';
You also need to change the call to new char[] in the assignment operator function too. You need:
str = new char[len+1];
#include<iostream>
using namespace std;
class MyString{
private:
char *str=new char[10];
public:
MyString(){*str='\0';} //default constructor
MyString(char *s){ //parameterized constructor
str=s;
}
private:
int length(char* s){
int i=0;
while(s[i]!='\0')
i++;
return i;
}
char* delchar(char* s,int count,int start){
int i,j=0;
char *temp= new char[10];
for(i=start;i<start+count;i++){
s[i]=' ';
}
for(i=0;i<length(s);i++){
if(s[i]!=' ')
temp[j++]=s[i];
}
s=temp;
return s;
}
public:
MyString operator-(MyString s){
int i=0,j=0,count=0,start=-1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str=str;
while(temp.str[i]!='\0'){
j=0;
start++;
while(s.str[j]!='\0'){
if(temp.str[i]==s.str[j]){
count++;
i++;
j++;
if(count==length(s.str)){//checks if the count
temp.str=delchar(temp.str,count,start);
i=i-count;
start=i-1;
count=0;
}
}
else{
i++;
count=0;
break;
}
}
}
return temp;
}
~MyString(){
delete str;
}
friend ostream &operator<<(ostream &stream,MyString& s){
stream<<s.str<<endl;
return stream;
}
};
int main(){
char *p= new char[20];
char *q= new char[10];
cin>>p;
cin>>q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3=s1-s2;
cout<<s3;
delete p;
delete q;
return 0;
}
The above code overloads the - operator .It tries to subtract the substring from the main string for example input1:joshmathews input2:josh output:mathews. I am trying to store the output in a new MyString object s3. When I use a destructor as shown above,outputting s3
returns null. But when I don't use a destructor I get the expected output.Can anyone help?
The primary issue is that Operater- returns a local object which is copied by a default copy constructor -- that default copy constructor points s3 to the exact same memory/buffer of the temp MyString, and when that temp is destructed, it wipes out the memory s3 is using.
That is referred to as a dangling pointer. You can read more here: http://en.wikipedia.org/wiki/Dangling_pointer#Cause_of_wild_pointers
The code below is the changes I made to get your program executing and returning a valid result, while completing without error. To be clear though, there are issues with this altered version, runtime error or no runtime error, but this will help illuminate some important points.
When you define a type that allocates memory, you've really started to do somethings that don't come free. My altered version below completely got rid of the destructor, so actually it leaks memory up until the program ends. The destructor was invalid though, so removing it allowed the program the finish. But this would be one of those things that in general you shouldn't just accept.
I added a copy constructor and a copy assignment operator:
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
Notice the strcpy_s in both of these. What is doing is copy the character string from the argument instead of just trying to point at the exact same string at the exact same address as the argument. If the argument gets destructed in your version, it wipes out some memory, so we can just accept default copy constructor and such since by default they are shallow copies that point to the same guts. That's one of the burdens of allocating memory -- you need to take care of that in both your destructor's ~and~ some of your constructors. This is referred to as "The Rule of Three":
If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.
Here's a wikipedia link about the rule of three: http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
You needed a destructor, so that's the clue you need the others. The reason is as I was saying -- by default you get a copy constructor for free, but it just makes everything point to the same guts if there are points are resources, but then you can't delete one without affecting everybody else.
Also, in delchar I added this line towards the bottom:
temp[j] = '\0';
Characters strings must always end at a null, and you copied only actual letter characters into temp, so without a null character, a strcpy doesn't know where to end.
If you remember I yanked out your destructor, then those are the changes I made.
The destructor also has an issue, in that if you use new to create an array, like
char* c = new char[10];
then you need to delete it in a way that indicates in was new'd as an array, like so:
delete [] c;
The next thing you should look into is how your MyString construction happens. If you step through, you'll see that the str member get new'd -- that means it holds a valid address into a buffer you can use -- but then if the MyString(char *s) constructor is used, it literally just takes this new address of s, and makes str hold that address instead -- which means the old address pointing to the valid buffer is lost, and that buffer of new'd memory cannot be freed. So that's a leak. You could use the strcpy_s strategy from the constructors I added to copy the contexts of what is pointed to by the s in MyString(char *s) into the new'd buffer. That would make a huge positive difference in the code.
And don't bother about all the elitists out there -- many of them were born doing headstands, so they can't see to relate to newbies giving things an honest effort.
Here's the full altered code:
#include<iostream>
using namespace std;
class MyString{
private:
char *str = new char[10];
public:
MyString(){ *str = '\0'; } //default constructor
MyString(char *s){ //parameterized constructor
str = s;
}
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
private:
int length(char* s){
int i = 0;
while (s[i] != '\0')
i++;
return i;
}
char* delchar(char* s, int count, int start){
int i, j = 0;
char *temp = new char[10];
for (i = start; i<start + count; i++){
s[i] = ' ';
}
for (i = 0; i<length(s); i++){
if (s[i] != ' ')
temp[j++] = s[i];
}
temp[j] = '\0';
s = temp;
return s;
}
public:
MyString operator-(MyString s){
int i = 0, j = 0, count = 0, start = -1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str = str;
while (temp.str[i] != '\0'){
j = 0;
start++;
while (s.str[j] != '\0'){
if (temp.str[i] == s.str[j]){
count++;
i++;
j++;
if (count == length(s.str)){//checks if the count
temp.str = delchar(temp.str, count, start);
i = i - count;
start = i - 1;
count = 0;
}
}
else{
i++;
count = 0;
break;
}
}
}
return temp;
}
~MyString(){
//delete str;
}
friend ostream &operator<<(ostream &stream, MyString& s){
stream << s.str << endl;
return stream;
}
};
int main(){
char *p = new char[20];
char *q = new char[10];
cin >> p;
cin >> q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3 = s1 - s2;
cout << s3;
delete p;
delete q;
return 0;
}
I ran this through debug, and in the String Substring function, everything works up until the return statement.
'returnString', in the code below, has the correct value when at the return line. However, as soon as I go to next line (the closing bracket directly after), it changes to:
{Text=0x003ed0e0 "îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþ... }String
which I traced back to the destructor, is the value the String has when deleted.
Now, I would have thought that the value would be deleted only after it's passed, but it seems like it's getting deleted first. You guys know how to fix this? Like I said above, the function works perfectly (at least, it looks like it), there's just something wrong with how it's returning the value.
The line that calls on my string function: String LocalString((Address.Substring(0, atIndex))); (Address is declared a String under 'private' in the respective header file)
Substring is a little past halfway down, right after the index operator. If it seems like I'm missing a function or a file, ask for it. Thanks for reading and (I'm hoping) the help.
The String.h file:
#pragma once
#include <iostream>
#include <sstream>
using namespace std;
// C++ String class that encapsulates an ASCII C-string
class String
{
public:
// Default constructor
String()
{
Text = NULL;
}
// MUST HAVE: Copy-constructor that performs deep copy
String(const String& source)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = source;
}
// Init-constructor to initialize this String with a C-string
String(const char* text)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = text;
}
// Destructor
~String()
{
delete[] Text;
}
// Returns the count of characters in a C-string text; NULL-terminator is not counted
static int GetLength(const char* text)
{
int x = 0;
while(text[x] != '\0')
x++;
return x;
}
// Assignment operator to perform deep copy
String& operator = (const char* text)
{
// Ddispose of old Text
delete[] Text;
// +1 accounts for NULL-terminator
int trueLength = GetLength(text) + 1;
// Dynamically allocate characters on heap
Text = new char[trueLength];
// Copy all characters from source to Text; +1 accounts for NULL-terminator
for ( int i = 0; i < trueLength; i++ )
Text[i] = text[i];
return *this;
}
// if length is not specified, then the substring spans from startPosition until the end of this String
// throws an exception when startPosition is out of bounds
String Substring(int startPosition, int length=0) const
{
char * str = this->GetText();
int strLength = length;
int x = 0;
char* substring = new char[strLength];
while(x < strLength && str[x+startPosition]!='\0')
{
substring[x] = str[x + startPosition];
x++;
}
substring[x]='\0';
String returnString = substring;
return returnString;
}
// Returns the count of characters in the String; NULL-terminator is not counted
int GetLength() const
{
return GetLength(Text);
}
private:
// The encapsulated C-string
char* Text;
};
somewhere in main.cpp...
String LocalString((Address.Substring(0, atIndex)));
String DomainString((Address.Substring(atIndex + 1)));
// or, in simpler syntax:
/*
* String hell0 = String andGoodbye;
* String smile = String andWave;
*/
Despite the comment // Assignment operator to perform deep copy the class doesn't have a user defined assignment operator.
The default, computer generated, assignment operator performs a shallow copy. The contained text will be lost as soon as one copy is destroyed.