I have doubt regarding copy constructor for a char* data member.
I have my code as,
#include<iostream>
using namespace std;
class Test
{
char *str;
int length;
public:
Test(){}
Test(char *a)
{
length = strlen(a)+1;
str = new char(length);
str = strncpy(str,a,length);
}
Test(const Test &t)
{
length = t.length;
if(t.str)
{
str = new char(length);
str = strncpy(str,t.str,length);
}
else
str = 0;
}
void out(){cout<<str;}
};
int main()
{
Test t("Test");
Test t1 = t;
t1.out();
return 0;
}
In case of constructor and copy constructor instead of using strncpy to copy the data member value, if I use:
Test(char *a)
{
str = new char();
str = a;
}
Test(const Test &t)
{
if(t.str)
{
str = new char();
str = t.str;
}
else
str = 0;
}
Will this also work? If yes, which method is preferable?
Not only the second version won't work, it will also provide you with a memory leak.
You first allocate memory to str, then you put in str the pointer pointing at the other object's string which means:
1. You no longer can access the memory you allocated.
2. Once you delete the string in one of the objects, it will be deleted in the other one too.
(*) you should also add an assignment operator.
In case of constructor and copy constructor instead of using strncpy to copy the data member value, if I use [code doing pointer copy]
I see that instead of strncpy you directly assign the pointer to the passed in string pointer. Will this work? No, what you're doing is just a pointer copy and not what the data the pointer is pointing to i.e. you need to deep copy and not shallow copy. You can use strncpy or std::copy_n.
Multiple other issues:
Use size_t instead of int to store sizes
Use constructor initilization list instead of assignment inside constructor body
Know the difference between new char [len] vs new char(); former allocates an array while the latter a char initialized to 0
Know that when you manage objects in free store, you've to implement an assignment operator, destructor, etc. a.k.a The Rule of Three/Five.
Allocating memory for a pointer and then losing its reference by allocating something else without releasing it will leak memory
Using std::string will be better since it's tried and tested
Related
class String
{
private:
char* ptr;
public:
String(const String& s1)
{
int len = strlen(s1.ptr);
ptr = new char[len+1];
strcpy(ptr,s1.ptr);
}
String(char* c)
{
int len = strlen(c);
ptr = new char[len+1];
strcpy(ptr,c);
}
~String()
{
cout<<"DELETING\n";
delete[] ptr;
}
void display()
{cout<<ptr;}
};
int main()
{
String s("Waqar"); String* s2 =&s;
String s1(s);
delete s2;
s1.display();
It goes fine till the second last line delete s2. While debugging, it throws an error to the effect of an unknown signal and never executes s1.display().
Being a noob, I was testing deep vs shallow copy concepts in c++ and thus written this junk.
Where did I go wrong?
s2 points to s. It is a pointer, and not a copy at all (shallow or otherwise). It was never allocated any memory by new. So when you try to delete s2, you are asking the program to free up memory on the stack that is not managed by new/delete. Do not delete s2 in this instance. It is an error. Your destructor is not at fault here.
Here's a sample program detailing how and when the destructor is
called a different points, and only delete's memory that was allocated with new.
// declare `copy`, but do not initialise it just yet. We want to
// initialise it from `s` (which is not yet declared)
String* copy;
{
// declare and initialise `s`, and copy "Wagner" into s.ptr
String s("Wagner");
// create a new String on the heap, initialised from `s`
// this is a deep copy because of how you wrote String(String&)
copy = new String(s);
s.display();
// `s` falls out of scope at the end of this block
// this means that s.~String() is invoked
}
// `copy` is unaffected as it was declared in the outer scope,
// and because it is a deep copy. Had it been a shallow copy
// then it would be broken as its `char* ptr` would not be valid
// any more.
copy->display();
// copy->~String() is invoked when copy is deleted
delete copy;
Can any one tell me how the char* member gets deleted after conversion constructor is called.
The output is Debug Assertion Failed
class String
{
public:
char* m;
String()
{
m = NULL;
}
String(const char* str)
{
m = strdup(str);
}
String(const String& I)
{
m = strdup(I.m);
}
~String()
{
delete m;
}
};
void main()
{
String s;
s = "abc";
s = "bcd";
}
The problem is that you have not implemented an assignment operator. So when you do this
s = "abc";
you end up with two String objects (one of them a temporary) holding a pointer to the same address. They both try to delete the same object. You need to follow the rule of three.
Note: as #kolrabi has pointed out, you should be calling free on a pointer allocated with strdup, not delete.
Let's analyze s = "abc":
First of all, this is an assignment, not an instantiation, because s has already been declared.
So the compilation solution for this would be to create a temporary String object with "abc" as the argument to the String constructor, and then call the String assignment operator in order to copy that temporary object into s.
Now, since you have not implemented an assignment operator for this class, the default assignment operator is called, and simply copies each one of the member variables from the temporary String object into s.
Finally, the temporary String object is destroyed and the memory pointed by its m variable is deallocated. As a result, you end up with s.m pointing to an invalid piece of memory.
I understand which is invoked in what situation...
Sample a;
Sample b = a; //calls copy constructor
Sample c;
c = a; //calls assignment operator
My question is Why these 2 different things exist at all? Why can't only one of the two take care of both situations?
No, they are different.
Copy constructor is used for constructing a new object (from another object). In this case you just need to initialize the members.
Assignment operator is used for an existing object (you may have constructed it by default constructor etc), and then assign it by another object. In this case you need to re-initialize members, sometimes means destroying and initializing them again.
Even so, the functionality of them are so similar, so you can share their implementation usually. Such as: What is the copy-and-swap idiom?
The copy constructor is invoked on creation, that means you don't have to take care of old resources in your object. The assignment operator on the other hand has to free the old resources. Besides they have different semantical meaning.
Suppose the String class:
class String
{
char *m_str;
size_t m_len, m_alloced;
public:
String(const char *str = "")
{
m_len = strlen(str);
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, str);
}
String(const String &other)
{
m_len = other.m_len;
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, other.m_str);
}
String &operator =(const String &rhs)
{
if (m_alloced < rhs.m_len + 1)
{
m_alloced = rhs.m_len + 1;
m_str = (char *)realloc(m_str, m_alloced);
}
m_len = rhs.m_len;
strcpy(m_str, rhs.m_str);
return *this;
}
const char *get() const
{
return m_str;
}
};
(live example)
Appearently, copy constructor and copy assignment operator does different things. Look at the String::String(const String &other).
m_len = other.m_len;
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, other.m_str);
It initializes its object. Set m_len, m_alloced, and m_str, and strcpy the string.
However, the String &String::operator =(const String &rhs) does -
if (m_alloced < rhs.m_len + 1)
{
m_alloced = rhs.m_len + 1;
m_str = (char *)realloc(m_str, m_alloced);
}
m_len = rhs.m_len;
strcpy(m_str, rhs.m_str);
return *this;
modify its object. Allocate more memory if required, and reset m_len and recopy the string.
Copy constructor is called when the object is created, and does initialize its object.
But copy assignment operator is called after the object is created, and does modify its object.
For example, look at this code.
String str1 = "asdf";
String str2 = str1;
String str3 = "12";
str3 = str1;
(it's also in the live example)
str1 is initialized by String::String(const char *). As you know, it'll contain "asdf".
str2 is initialized by copy constructor, String::String(const String &other). By the copy constructor, str2 will contain the same content.
str3 is initialized by String::String(const char *), like str1. However, in line 4, it is modified by copy assignment operator. So, str3 contains "12" at first, but its content will be modified into "asdf", by copy assignment operator.
Well, you could technically get away with just having a copy-constructor and a destructor, but that would require that every time you wanted to do an assignment you would have to destroy the object and then reconstruct it. This would become HIGHLY inefficient in the vast majority of use cases.
We cannot just have an operator= either, as you have no idea what the left-hand-side of the operator will be if it has not yet been constructed. It's members would probably contain garbage data and "shit would hit the fan".
Thus, you can think of the need for both an operator= and a copy-constructor as an optimization.
That being said there are a few design-patterns that can reduce the amount of code you have to write. For example, assuming you have a copy-constructor, a destructor and a swap-function implemented for your class. You could implement operator= using a copy-swap operation:
MyClass & operator=( MyClass rhs ) { // notice the call by value, this will
// implicitly call the copy-constructor.
swap(*this, rhs);
return *this;
}
Whilst this operator has strong exception safety, it is worth to notice that it may be much less efficient than reusing preallocated resources (which a more advanced operator= might do).
Basically, they are used in different situations, many correct answer, i just wanna add something to clear out where the copy constructor is also used:
For example:
void test(Fraction in){
//Do something here
}
Fraction test2(){
Fraction a(1, 2);
//Do something here
return a; // construct a return value by copy a
}
int main()
{
Fraction a(2, 3);
Fraction b(a); //construct b by copy a
Fraction c=a; //construct c by copy a
test(a); //construct in by copy a
}
Memory Management : arrays and dynamic allocation with new operator
Q. In terms of Memory Management, what error does occur?
class String
{
public:
String(const char right[]);
String& operator= (const String& right);
int length() const;
private:
char* buffer;
int len;
}
int String::length() const {return len;}
String::String(const char right[])
{
len = 0;
while (right[len] != '\0')
len++;
buffer = new char[len+1];
for (int i = 0; i < len; i++)
buffer[i] = right[i];
buffer[len] = '\0';
}
String& String::operator= (const String& right)
{
if (this != &right)
{
delete[] buffer;
len = right.length();
char* buffer = new char[len + 1];
for (int i = 0; i < len; i++)
buffer[i] = right[i];
buffer[len] = '\0';
}
return *this;
}
Answer.
I have no clue... Could you help me?
This one seems ok too.
new and it is also deleted.
Where is the memory leak?
Please let me know.
Thanks,
You need to provide a constructor which allocates the pointer member(infact all constructors of your class should do that) and destructor which deallocates it.
Also, You need to provide a copy constructor which performs a deep copy of the pointer member.
String::String(const String& obj)
{
....
}
Good Read:
What is The Rule of Three?
Also, String is an awful name for a class especially since there exists a std::string.
Rule of three: if a class defines a destructor or a copy constructor or a copy assignment operator, it probably has to define all three.
Your code vioaltes this rule by not providing a destructor and a copy constructor while providing a copy assignment operator.
There are 4 issues with your code.
Your assignment operator has the following line: char* buffer = new char[len + 1]; This is declaring a new pointer variable and not using the class member. This will allocate memory to a pointer that then goes out of scope and can never be deleted and will be a memory leak and make your class not function.
The lack of a destructor means that your class leaks memory. Your destructor is responsible for freeing the memory that your are allocating with new[].
The lack of a copy constructor means that your String class cannot be used in many situations including in an STL container. The STL containers require that types have a public copy constructor, assignment operator and desctructor.
You are not enforcing your class invariants in the face of exceptions. The goal is to make your class exception safe. In the changes I made I tried to achieve the strong exception guarantee and conserve the class state in the face of exceptions. Considering exception safety is an important aspect of creating your own classes - especially library classes such as String.
Here's the code that attempts fixes all of those issues. To keep the code short I am putting the implementation into the class rather than separating it. Of course, in real code you should use std::string.
class String
{
public:
String() : len(0), buffer(nullptr)
{}
String(const char right[]) : len(0), buffer(nullptr)
{
if(!right)
return;
int temp_len = strlen(right);
buffer = new char[temp_len+1];
len = temp_len; // only set the length after the allocation succeeds
strcpy(buffer, right);
}
// a copy constructor is essential for this class
String(const String& right) : len(0), buffer(nullptr)
{
String temp(right.buffer);
swap(temp);
}
// must have a destructor to avoid leaks
~String()
{
if(buffer) // only delete if it has been allocated
delete[] buffer;
}
String& operator= (const String& right)
{
String temp(right.buffer);
swap(temp);
return *this;
}
int length() const
{
return len;
}
private:
void swap(String& rhs)
{
std::swap(buffer, rhs.buffer);
std::swap(len, rhs.len);
}
char* buffer;
int len;
};
When your String objects are destroyed, the default constructor is called since you did not define one. The default destructor will not delete[] your char array for you, you must declare and define a destructor to do this.
Also, you will find that using strcpy()/strncpy() is much faster than a simple loop to copy each char. (at least when compiling with GCC).
OK, very simple String class, which holds constant strings (i.e. can't be changed once initialized), implements the copy ctor and concatenation function conc. It's that function that's giving me trouble, because I really cannot figure out why a local variable I make isn't passed normally as return value.
Code:
class String
{
public:
String(char*);
String(const String& other);
~String();
friend String conc(const String&, const String&);
friend std::ostream& operator<<(std::ostream&, const String&);
private:
const char* const str;
};
String::String(char* aStr) : str(aStr) {}
String::String(const String& other) : str(other.str) {}
std::ostream& operator<<(std::ostream& out, const String& s)
{
out<<s.str<<"\n";
return out;
}
String::~String()
{
delete str;
}
String conc(const String& s1, const String& s2)
{
int n = strlen(s1.str) + strlen(s2.str);
int i, j;
char* newS = new char[n+1];
for(i = 0; i < strlen(s1.str); ++i)
{
newS[i] = s1.str[i];
}
for(j = 0; j < strlen(s2.str); ++j)
{
newS[i+j] = s2.str[j];
}
newS[i+j] = '\0'; //newS is made correctly
String tmp(newS); // the tmp object is made correctly
return tmp; // here tmp becomes something else --- Why??
}
int main()
{
String s1("first");
String s2("SECOND");
String s3 = conc(s1, s2); //here the copy ctor should be called, right?
std::cout<<s3;
_getch();
}
As you can see in the comments, the problem is in the conc function at the end. I have made the function return a String, and not String& on purpose, given that the value it returns shouldn't be an lvalue...
Please explain & help, thanks!:)
There are memory management problems all over the place here. Each String object should own its own memory. That means that String::String(char*) needs to allocate an array of char and copy the contents of the input string; String::String(const String&) needs to allocate an array of char and copy the contents of the input string; and String::operator= needs to delete its own string (unless it's the same as the input string), allocate an array of char, and copy the contents of the input string. Finally, String::~String() should delete[] the array of char.
It is not what you think: the problem is the deletion of the temp. You call delete instead of delete[] on an array allocated with new[] - an undefined behavior.
Once you fix that error, you will have other errors related to Strings initialized with string literals: passing them to delete[] is also undefined behavior.
The root cause of the problem is that your class does not let you differentiate between the memory that you must release and the memory that you must not release. You should do it uniformly, for example by always copying the content into an array that you allocate inside the constructor.
There are several problems with your class:
Your String( char * ) constructor assumes ownership of the pointer passed to it, so if you construct the object with a string literal the destructor will try to delete it, leading to undefined behavior.
Your copy constructor assumes ownership of the string belonging to the object being copied, instead of making its own copy, due to which the string will be double deleted.
In the conc function you allocate memory using new[] but then you delete it instead of delete[] leading to undefined behavior
The member variable str is supposed to be a char array, so the destructor must delete[] it instead of delete
You need to change your constructor so it's making a copy of the C string, for example:
String::String(const String& other) : str(strdup(other.str)) {}
If you use strdup above you should change your destructor appropriately, so instead of using delete you use free
String::~String() { free(str); }
It would be a good idea to change your other constructor so it's not acquiring the C string, but making a copy of it as well, this way behaviour of all constructors would be more consistent and safer, in general:
String::String(const char* aStr) : str(strdup(aStr)) {}
If you make it this way, it will work correctly whether the client code is passing you pointer allocated with malloc or new.
Replacing strdup and free with more new and delete should be easy, I'm leaving it to you as an exercise.
When you return a temporary from a function, a copy is made for the return value and the temporary is destroyed. (Sometimes the copy can be skipped via return value optimization but I don't think that's happening here).
Because your copy constructor copies the character array pointer, both objects are now pointing to the same memory. When the temporary is destroyed, it destroys the character array and now the returned object has a dangling pointer.
One of the big benefits of an immutable string class is that it can easily share buffers as you do here, without the overhead of copying. You just need a mechanism for counting the references to the buffer so it isn't deleted until the last object is deleted. You can use a std::shared_ptr with a custom deleter instead of the char * pointer.
You should also look into the Rule of Three to make sure you're implementing all the necessary functions.