我有一个类A,它在构造函数中为一个整数(由类memeber指出_pPtrMem指向)动态分配内存,并在析构函数中释放相同的内容。为了避免Shallow复制,我重载了赋值运算符和复制构造函数。赋值运算符过载的广泛使用方式如下:
A& operator = (const A & iToAssign)
{
if (this == & iToAssign) // Check for self assignment
return *this;
int * pTemp = new int(*(iToAssign._pPtrMem)); // Allocate new memory with same value
if (pTemp)
{
delete _pPtrMem; // Delete the old memory
_pPtrMem = pTemp; // Assign the newly allocated memory
}
return *this; // Return the reference to object for chaining(a = b = c)
}
实现相同的另一种方法可能是
A& operator = (const A & iToAssign)
{
*_pPtrMem= *(iToAssign._pPtrMem); // Just copy the values
return *this;
}
由于第二个版本相对简单得多,速度更快(没有解除分配,内存分配),为什么不广泛使用?我无法解决的任何问题?
此外,我们从赋值运算符返回相同类型的对象进行链接(a = b = c)...所以不是返回 *这个就可以返回 iToAssign 对象,因为两个对象现在都相等?
答案 0 :(得分:6)
通常,实现复制赋值运算符的最佳方法是为您的类提供swap()
函数(如果符合您的要求,则使用标准函数),然后通过副本实现复制赋值运算符构造:
A& A::operator= (A iToAssign) // note pass by value here - will invoke copy constructor
{
iToAssign.swap(*this);
return *this;
}
// void swap(A& other) throws() // C++03
void A::swap(A& other) noexcept
{
std::swap(_pPtrMem, other._pPtrMem);
}
这可以确保您的复制赋值运算符和复制构造函数永远不会发生分歧(也就是说,您不能更改一个并忘记更改另一个)。
答案 1 :(得分:1)
第一个版本用于_pPtrMem
是指向某些基本类型的指针,例如动态分配的数组。如果指针指向具有正确实现的赋值运算符的单个对象,则第二个版本将同样好。但在这种情况下,我认为你根本不需要使用指针。
答案 2 :(得分:1)
不,您的实施没有问题。但是分配一个整数动态至少非常特殊。
此实现未广泛使用,因为没有人在免费商店上分配单个整数。对于在编译时未知的可变长度的数组,通常使用动态分配的内存。在这种情况下,大多数情况下使用std :: vector是个好主意。
不,它不好,返回一个不同的对象。身份与平等不同:
T a, b, d;
T& c = a = b;
c = d; // should change a, not b
您是否希望第三行改变b?
或更好的事件示例:
T a;
T& b = a = T();
这将导致悬挂引用,引用临时和被破坏的对象。
答案 3 :(得分:0)
在第二种情况下,如果_pPtrMem
最初未取消分配,则行
*_pPtrMem= *(iToAssign._pPtrMem); // Just copy the values
导致分配到无效的内存位置(可能是分段错误)。只有在此次调用之前已为_pPtrMem
分配内存时,此操作才有效。
答案 4 :(得分:0)
在这种情况下,第二种实施方式是更好的。 但是在对象中使用动态的通常原因是因为 大小可能会有所不同。 (另一个原因是因为你想要 浅拷贝的参考语义。)在这种情况下, 最简单的解决方案是来使用你的第一个任务(没有 测试自我分配)。有人可能根据具体情况而定 考虑重新使用新值时已经存在的内存 会合适;它有点增加了复杂性(因为你必须这样做) 测试它是否合适,并且仍然可以 分配/复制/删除,如果没有),但它可以改善 在某些情况下的表现。