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
}
Related
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 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
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).
Our professor posted a custom 'String' template file online, and asked us a while ago to fill out the functions below. My question, in order to try and understand this, is why the top three constructors have Text = NULL; and below it, this = source;, some other form of it. I feel like each should say Text = the_input_parameter.
Many thanks, here's the code:
class String
{
public:
// Default constructor
String()
{
Text = NULL;
}
String(const String& source)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = source;
}
String(const char* text)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = text;
}
~String()
{
delete[] Text;
}
// 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;
}
// Returns a reference to a single character from this String
char& operator [] (int index) const
{
int length = GetLength();
// Check for valid index
if ( (index < 0) || (index > length) )
{
stringstream error;
error << "operator[] - index " << index << " is out of bounds (0.." << (length - 1) << ")";
throw String(error.str().c_str());
}
return Text[index];
}
private:
// The encapsulated C-string
char* Text;
};
Why you should not implement constructors in terms of assignment:
It gets pretty nasty in derived classes. Think about it.
It's difficult to make exception safe.
It's also inefficient, to boot (requiring default construction then assignment).
So the answer to why it’s done that way in your example code, is possibly that your professor doesn’t know diddly about C++ programming.
Otherwise, it's difficult to say: it just doesn't make any sense at all to do that.
Going the other way, however, namely implementing copy assignment in terms of copy construction, is very common and is known as the copy-and-swap idiom.
It's simple, exceptions safe and generally efficient, and goes like this:
class Foo
{
public:
void swap_with( Foo& other ) throw()
{
// No-throwing swap here.
}
void operator=( Foo other )
{
other.swap_with( *this );
}
};
yup, that's all.
variants include naming the swapper just swap, and letting the assignment operator return a reference, and some prefer to pass the argument by reference and then make a copy (using copy construction).
It's just a way to factor out common code into a helper function. In this case, operator=() acts as the helper function. Its function is to deallocate the current string (in this case, NULL) and perform a deep copy of the right-hand side.
I feel like each should say Text = the_input_parameter.
For String(const String& source) this wouldn't compile since source isn't the right type.
For String(const char* text) this wouldn't be correct since that would just assign the pointer instead of performing a deep copy.
The above assumes you are only showing us a part of the class, and that the actual class defines an appropriate assignment operator and a destructor. If it doesn't, you need a new professor.
The class manages memory, so destructor releases it, and assignment operator allocates new for new data and releases old one (shoud be in that order).
Then the explanation of initial assignment is clear: you need to initialize the member field to a correct value, as otherwise it will contain garbage (some pointer to somewhere), which code will attempt to use and free.
Though it is not seen from the code, there might be also assignment for const String& and type cast operator operator const char *() const.
I am trying to overload operator + for my class as follows:
MyClass MyClass::operator +(const MyClass& rval) const {
MyClass ret(m_src); // m_src is member of MyClass: char* m_src;
ret.Add(rval); // this->m_src + rval, this method work correctly
return ret; // so, in ret.m_src I have correct value
} // but after this C++ call destructor for ret
Destructor:
delete[] m_src; // because in some methods I allocate dynamic memory
So, destructor clear memory and function return trash. How I can avoid this situation?
If I delete destructor, function work normal, but in this case I have memory leak :(
P.S: I can't change prototype of overloading +, unfortunately. Thanks.
You avoid this by replacing:
char *m_src;
by:
std::string m_src;
in your class MyClass.
With char * as a class member what you get is easy to go mess up manual memory management, you simply replace that with implicit RAII based memory management of std::string and save yourself all those troubles. And this is the very purpose of existence of std::string in C++.
If you cannot use standard library std::string class(though I simply fail to understand why), You need to make sure you are following the Rule of Three.
What you need is a copy constructor that copies m_src
MyClass::MyClass( const MyClass& ref ) {
m_src = new char[/*get len from ref*/];
memcpy( m_src, ref.m_src, /* len from ref */ * sizeof(char) );
}
Now you can change the operator+ to work with the copy constructor
MyClass MyClass::operator +(const MyClass& rval) const {
MyClass ret(rval); // use copy constructor
ret.Add(rval); // this->m_src + rval, this method work correctly
return ret; // so, in ret.m_src I have correct value
}