Calling Assignment Operator in Copy Constructor

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



Leave a reply



Submit