Calling assignment operator in copy constructor
Yes, that's a bad idea. All member variables of user-defined types will be initialized first, and then immediately overwritten.
That swap trick is this:
Foo& operator=(Foo rhs) // note the copying
{
rhs.swap(*this); //swap our internals with the copy of rhs
return *this;
} // rhs, now containing our old internals, will be deleted
call copy constructor from assignment operator function
The offending line isn't what you think it is. It actually declares a variable other
of type FeatureValue
. This is because constructors to not have names and cannot be called directly.
You can safely invoke the copy assignment operator from the constructor as long as the operator is not declared virtual.
FeatureValue::FeatureValue(const FeatureValue& other)
: m_value(nullptr), m_size(0)
{
*this = other;
}
// assignment operator function
FeatureValue& FeatureValue::operator=(const FeatureValue& other)
{
if(this != &other)
{
// copy data first. Use std::unique_ptr if possible
// avoids destroying our data if an exception occurs
uint8_t* value = new uint8_t[other.m_size];
int size = other.m_size;
for (int i = 0; i < other.m_size; i++)
{
value[i] = other.m_value[i];
}
// Assign values
delete[] m_value;
m_value = value;
m_size = size;
}
return *this;
}
This will works just dandy or you can use the typical guidelines for the copy & swap idiom suggested in Vaughn Cato's answer
Does the assignment operator call copy constructor?
b=a;
Here also bit by bit copy is done (a.p and b.p pointing to same location), it does not invoke copy constructor because constructor is called when b in defined (default constructor).so you have to overload =
operator
test &operator =(const test &src)
{
*this->p=*src.p; //copy value not address
return *this;
}
Add this to your class test and you need to check the memory is allocated or not by new because new can fail to allocate requested memory.
But here the copy constructor is called
test c=a;
Using copy constructor in assignment operator
Here is the copy/swap idiom on your example in a nutshell:
#include <algorithm>
class Obj
{
int *p;
void swap(Obj& left, Obj& right);
public:
Obj(int x = 0) : p(new int(x)) {}
Obj(const Obj& s);
Obj& operator = (const Obj& s);
~Obj() { delete p; }
};
Obj::Obj(const Obj& source) : p(new int(*source.p))
{}
void Obj::swap(Obj& left, Obj& right)
{
std::swap(left.p, right.p);
}
Obj & Obj::operator=(const Obj & source)
{
Obj temp(source);
swap(*this, temp);
return *this;
}
int main()
{
Obj o1(5);
Obj o2(o1);
Obj o3(10);
o1 = o3;
}
To see how it works, I purposefully created a member that is a pointer to dynamically allocated memory (this would be problematic if there were no user-defined copy constructor and assignment operator).
If you focus on the assignment operator, it calls the Obj
copy constructor to construct a temporary object. Then the Obj
-specific swap
is called that swaps the individual members. Now, the magic is in the temp
object after the swap
is called.
When the destructor of temp
is called, it will call delete
on the pointer value that this
used to have, but was swapped out with the temp
pointer. So when temp
goes out of scope, it cleaned up the memory allocated by the "old" pointer.
Also, note that during assignment, if new
throws an exception during the creation of the temporary object, the assignment will throw an exception before any members of this
become changed. This prevents the object from having members that may be corrupted due to having them changed inadvertently.
Now, a previous answer was given that uses the often-used "shared code" approach to copy assignment. Here is a full example of this method, and an explanation of why it has issues:
class Obj
{
int *p;
void CopyMe(const Obj& source);
public:
Obj(int x = 0) : p(new int(x)) {}
Obj(const Obj& s);
Obj& operator = (const Obj& s);
~Obj() { delete p; }
};
void Obj::CopyMe(const Obj& source)
{
delete p;
p = new int(*source.p);
}
Obj::Obj(const Obj& source) : p(0)
{
CopyMe(source);
}
Obj & Obj::operator=(const Obj & source)
{
if ( this != &source )
CopyMe(source);
return *this;
}
So you would say "what's wrong with this?" Well, the thing that's wrong is that CopyMe
's first thing it does is call delete p;
. Then the next question you'll ask is "So what? Isn't that what we're supposed to do, delete the old memory?"
The issue with this is that there is a potential for the subsequent call to new
to fail. So what we did was destroy our data before we even know that the new data will be available. If new
now throws an exception, we've messed up our object.
Yes, you can fix it easily by creating a temporary pointer, allocating, and at the end, assign the temp pointer to p
. But many times this can be forgotten, and code such as above remains in the codebase forever, even though it has a potential corruption bug.
To illustrate, here is the fix for the CopyMe
:
void Obj::CopyMe(const Obj& source)
{
int *pTemp = new int(*source.p);
delete p;
p = pTemp;
}
But again, you will see tons of code that use the "shared code" method that has this potential bug and not mention a word about the exception issue.
copy constructor and assignment operator in C++
Constructors, including copy constructors, are for initializing a class object. Assignment operators are for modifying an object which was already initialized.
Line (1) calls a copy constructor because it is initializing the object second
. The assignment operator has nothing to do with the fact that the =
symbol is also used in this syntax. Line (2) assigns to first
which already exists, so uses the assignment operator.
In C++14 and earlier for line (3), the compiler is allowed to create a temporary object for the MyClass(2)
expression, then use the copy constructor to initialize third
, then destroy the temporary. But the compiler is also allowed to "elide" (remove) the temporary object, the copy constructor, and the temporary destructor, just initializing third
directly in the same way as the original temporary. This copy elision is the one case where C++ allows an optimization which can cause a difference in observable behavior, since the copy constructor and/or destructor might have side effects (like printing traces) which the optimization skips.
In C++17 and later for line (3), we say that the expression MyClass(2)
has result object third
. So the expression specifies that third
is direct-initialized with argument 2
. No copy constructor or temporary object is involved. This feature of C++17 is often called "mandatory copy elision", since the behavior is the same as a C++14 program where the compiler was able to apply the copy elision optimization. But technically it's not a copy elision, since there is no copy involved to elide. There are still some other cases where copy elision is optional for the compiler.
calling copy constructor in assignment operator
The assignment operator you found is not correct. All it does is make a copy of eSource
, but it's supposed to modify the object on which it is called.
The compiler-generated assignment operator for the class is equivalent to:
Example &operator= (const Example &eSource)
{
pValue = eSource.pValue;
return *this;
}
For this class there's no point implementing operator=
, since the compiler-generated version basically cannot be improved on. But if you do implement it, that's the behaviour you want even if you write it differently.
[Alf will say return void
, most C++ programmers will say return a reference. Regardless of what you return, the vital behaviour is an assignment to pValue
of the value from eSource.pValue
. Because that's what the copy assignment operator does: copy from the source to the destination.]
Related Topics
Order of Constructor Call in Virtual Inheritance
Unresolved Externals in C++ When Using Vectors and Find
How to Check That an Element Is in a Std::Set
How to Check Whether Multiple Variables Are Equal to the Same Value
How to (Un)Escape Strings in C/C++
Cyclic Dependency Between Header Files
Std::Thread Is Not a Member of Namespace Std Using Eclipse Kepler Mingw
Why Does Gcc Allow Char Array Initialization with String Literal Larger Than Array
How to Building Static Qt with Static Openssl
Heap Corruption While Using Createwindowexw
Compute Objects Moving with Arrows and Mouse
Semicolon After Class Declaration Braces
Is !! a Safe Way to Convert to Bool in C++