I wrote up this code after reading item 11 of Effective C++ ( Third Edition ).
#include <iostream>
using namespace std;
#define MAX_COLORS 20
class Widget
{
public:
Widget ( int seed );
~Widget ( );
Widget& operator=( const Widget& rhs );
void ToString ( );
private:
Widget& SelfAssignmentUnsafe ( const Widget& rhs );
Widget& SelfAssignmentSafe ( const Widget& rhs );
Widget& SelfAssignmentAndExceptionSafe ( const Widget& rhs );
void MakeDeepCopy ( const Widget& rhs );
int *colorPallete;
};
void Widget::ToString()
{
int i = 0;
for ( i = 0; i < MAX_COLORS; i++ )
{
cout << "colorPallete[" << i << "]: " << colorPallete[i] << endl;
}
}
Widget::Widget ( int seed ):
colorPallete ( new int[MAX_COLORS])
{
int i = 0;
for ( i = 0; i < MAX_COLORS; i++ )
{
colorPallete[i] = seed + i;
}
}
Widget& Widget::operator=( const Widget& rhs )
{
// return SelfAssignmentUnsafe ( rhs );
// return SelfAssignmentSafe( rhs );
return SelfAssignmentAndExceptionSafe ( rhs );
}
Widget& Widget::SelfAssignmentUnsafe ( const Widget& rhs )
{
delete[] colorPallete;
colorPallete = 0;
MakeDeepCopy( rhs );
return *this;
}
Widget& Widget::SelfAssignmentSafe ( const Widget& rhs )
{
if ( this == &rhs ) return *this;
delete[] colorPallete;
colorPallete = 0;
MakeDeepCopy ( rhs );
return *this;
}
void Widget::MakeDeepCopy ( const Widget& rhs )
{
int i = 0;
colorPallete = new int [MAX_COLORS];
for ( i = 0;i < MAX_COLORS; i++ )
{
colorPallete[i] = rhs.colorPallete[i];
}
}
Widget& Widget::SelfAssignmentAndExceptionSafe ( const Widget& rhs )
{
int *origColorPallete = colorPallete;
MakeDeepCopy ( rhs );
delete[] origColorPallete;
origColorPallete = 0;
return *this;
}
Widget::~Widget()
{
delete[] colorPallete;
}
int main()
{
Widget b(10);
Widget a(20);
b.ToString();
b = b;
cout << endl << "After: " << endl;
b.ToString();
}
The author talks about handling assignment to self in the assignment operator:
Widget a(10);
a = a;
From the assignment operator for Widget I call Widget::SelfAssignmentAndExceptionSafe.
In Widget::SelfAssignmentAndExceptionSafe the idea is to save the colorPallete pointer in origColorPallete. Then make a deep copy of rhs.colorPallete. When the copy succeeds I delete the original pointer and return reference to self.
The above mechanism is supposed to be self assignment and exception safe.
However, Widget::SelfAssignmentAndExceptionSafe is not able to handle assignment to self properly. The colorPallete array contains junk after self-assignment. Its handling the other cases very well.
Why could this be?
Please help.
[EDIT: After examining all the answers]
Thanks for your answers. I have updated the MakeDeepCopy method and the example's working fine now. Below, I have pasted the updated code:
#include <iostream>
using namespace std;
#define MAX_COLORS 20
class Widget
{
public:
Widget ( int seed );
~Widget ( );
Widget& operator=( const Widget& rhs );
void ToString ( );
private:
Widget( Widget& rhs );
Widget& SelfAssignmentUnsafe ( const Widget& rhs );
Widget& SelfAssignmentSafe ( const Widget& rhs );
Widget& SelfAssignmentAndExceptionSafe ( const Widget& rhs );
void MakeDeepCopy ( const int* rhs );
int *colorPallete;
};
void Widget::ToString()
{
int i = 0;
for ( i = 0; i < MAX_COLORS; i++ )
{
cout << "colorPallete[" << i << "]: " << colorPallete[i] << endl;
}
}
Widget::Widget ( int seed ):
colorPallete ( new int[MAX_COLORS])
{
int i = 0;
for ( i = 0; i < MAX_COLORS; i++ )
{
colorPallete[i] = seed + i;
}
}
Widget& Widget::operator=( const Widget& rhs )
{
// return SelfAssignmentUnsafe ( rhs );
// return SelfAssignmentSafe( rhs );
return SelfAssignmentAndExceptionSafe ( rhs );
}
Widget& Widget::SelfAssignmentUnsafe ( const Widget& rhs )
{
delete[] colorPallete;
colorPallete = 0;
MakeDeepCopy( rhs.colorPallete );
return *this;
}
Widget& Widget::SelfAssignmentSafe ( const Widget& rhs )
{
if ( this == &rhs ) return *this;
delete[] colorPallete;
colorPallete = 0;
MakeDeepCopy ( rhs.colorPallete );
return *this;
}
void Widget::MakeDeepCopy ( const int* rhs )
{
int i = 0;
colorPallete = new int [MAX_COLORS];
for ( i = 0;i < MAX_COLORS; i++ )
{
colorPallete[i] = rhs[i];
}
}
Widget& Widget::SelfAssignmentAndExceptionSafe ( const Widget& rhs )
{
int *origColorPallete = colorPallete;
MakeDeepCopy ( rhs.colorPallete );
delete[] origColorPallete;
origColorPallete = 0;
return *this;
}
Widget::~Widget()
{
delete[] colorPallete;
}
int main()
{
Widget b(10);
Widget a(20);
b.ToString();
b = b;
cout << endl << "After: " << endl;
b.ToString();
}
[EDIT: Modified code based on Charles's response ]
The idea is to implement "copy-and-swap" idiom to make code both self assignment and exception safe. Note that copy is implemented only in the copy constructor. If the copy succeeds we swap in the assignment operator.
Another improvement over the previous update is that MakeDeepCopy's interface depended on correct usage. We had to store/delete the colorPallete pointer before calling MakeDeepCopy. No such dependencies exist now.
#include <iostream>
using namespace std;
#define MAX_COLORS 20
class Widget
{
public:
Widget ( int seed );
~Widget ( );
Widget& operator=( const Widget& rhs );
void ToString ( );
Widget( const Widget& rhs );
private:
int *colorPallete;
};
void Widget::ToString()
{
int i = 0;
for ( i = 0; i < MAX_COLORS; i++ )
{
cout << "colorPallete[" << i << "]: " << colorPallete[i] << endl;
}
}
Widget::Widget ( int seed ):
colorPallete ( new int[MAX_COLORS])
{
int i = 0;
for ( i = 0; i < MAX_COLORS; i++ )
{
colorPallete[i] = seed + i;
}
}
Widget::Widget( const Widget& rhs ):
colorPallete( new int[MAX_COLORS] )
{
std::copy ( rhs.colorPallete, rhs.colorPallete + MAX_COLORS, colorPallete );
}
Widget& Widget::operator=( const Widget& rhs )
{
Widget tmp(rhs);
std::swap ( colorPallete, tmp.colorPallete );
return *this;
}
Widget::~Widget()
{
delete[] colorPallete;
}
int main()
{
Widget b(10);
Widget a(20);
b.ToString();
b = b;
cout << endl << "After: " << endl;
b.ToString();
}