我在阅读《Effective C++(第三版)》的第11条建议后,写出了这段代码。
#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();
}
作者谈论了在赋值运算符中如何处理自我赋值的问题:
Widget a(10);
a = a;
我从Widget的赋值运算符中调用Widget::SelfAssignmentAndExceptionSafe。
在Widget::SelfAssignmentAndExceptionSafe中,想法是将colorPallete指针保存在origColorPallete中。然后对rhs.colorPallete进行深度复制。当复制成功时,删除原始指针并返回对自身的引用。
上述机制应该是自我赋值和异常安全的。
然而,Widget::SelfAssignmentAndExceptionSafe不能正确处理对自身的赋值。自我赋值后,colorPallete数组包含垃圾数据。它很好地处理了其他情况。
为什么会这样呢?
请帮忙解决。
[编辑:检查了所有答案后]
感谢您的回答。我已更新了MakeDeepCopy方法,示例现在可以正常工作。下面,我粘贴了更新后的代码:
#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();
}
[编辑:根据Charles的回复修改了代码]
这个想法是实现“复制并交换”惯用语,使代码既可以进行自我分配,又可以进行异常安全。请注意,仅在复制构造函数中实现复制。如果复制成功,我们会在赋值运算符中进行交换。
与上次更新相比的另一个改进是MakeDeepCopy的接口依赖于正确的使用。我们必须在调用MakeDeepCopy之前存储/删除colorPallete指针。现在不存在这样的依赖关系。
#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();
}