从operator=中“自我赋值”看new的抛出异常

本文探讨了C++中自我赋值的问题,特别是当对象内部使用动态分配时可能遇到的问题。通过示例展示了传统的"证同测试"和优化后的赋值顺序以避免错误。同时,讨论了new操作在分配失败时会抛出异常而非返回空指针的情况,提出通过捕获异常或抑制new异常的方法来处理这种情况。
摘要由CSDN通过智能技术生成

在Effective C++中看到这个问题时才发现以前写的代码完全没有注意过这个问题

“自我赋值”发生在对象被赋值给对象本身时,例如:

class Weight
{
  ...};
...
w=w;

虽然这看上去确实非常的蠢,但是谁也不能保证这不会发生,毕竟这是合法的,但是问题出来了,我们写的显示赋值函数一般是这样的,假设我们在类中使用了动态分配。

class Bitmap{
  ...};
class Weight
{
...
private:
Bitmap * _pb;
};
Weight & Weight::operatot=(const Weight & rhs)
{
delete _pb;
_pb=new Bitmap(rhs._pb);
return *this;
}

好的,现在面临的问题是,*this和rhs这两个指向了同一个对象,在执行delete _pb 时同时也将rhs的Bitmap销毁了。

对此提出了两种有效的解决方法

①传统做法在operator=之前做一个“证同测试(identity test)”

Weight & Weight::operatot&
下面是CXMUVector类模板的设计实现: ```cpp #include <iostream> #include <stdexcept> template <typename T> class CXMUVector { public: // 构造函数 CXMUVector(int c = 100) : capacity(c), size(0) { pdata = new T[capacity]; } // 拷贝构造函数 CXMUVector(const CXMUVector& other) : capacity(other.capacity), size(other.size) { pdata = new T[capacity]; for (int i = 0; i < size; i++) { pdata[i] = other.pdata[i]; } } // 析构函数 ~CXMUVector() { delete[] pdata; } // 重载运算符[] T& operator[](int index) { if (index >= size) { throw std::out_of_range("Index out of range!"); } return pdata[index]; } // 重载运算符= CXMUVector& operator=(const CXMUVector& other) { if (this != &other) { delete[] pdata; capacity = other.capacity; size = other.size; pdata = new T[capacity]; for (int i = 0; i < size; i++) { pdata[i] = other.pdata[i]; } } return *this; } // PushBack函数 void PushBack(const T& element) { if (size >= capacity) { int newCapacity = capacity * 2; T* newData = new T[newCapacity]; for (int i = 0; i < size; i++) { newData[i] = pdata[i]; } delete[] pdata; pdata = newData; capacity = newCapacity; } pdata[size++] = element; } // PopBack函数 T PopBack() { if (size == 0) { throw std::string("Pop On Empty Vector!"); } return pdata[--size]; } private: int capacity; // 容量 int size; // 元素个数 T* pdata; // 数据存储位置的指针 }; int main() { try { CXMUVector<int> myVector(5); myVector.PushBack(1); myVector.PushBack(2); myVector.PushBack(3); std::cout << "Vector elements: "; for (int i = 0; i < myVector.size; i++) { std::cout << myVector[i] << " "; } std::cout << std::endl; int poppedElement = myVector.PopBack(); std::cout << "Popped element: " << poppedElement << std::endl; std::cout << "Vector elements after popping: "; for (int i = 0; i < myVector.size; i++) { std::cout << myVector[i] << " "; } std::cout << std::endl; // 测试异常捕捉 int poppedElement2 = myVector.PopBack(); // Pop the last element int poppedElement3 = myVector.PopBack(); // Pop on empty vector, throws exception } catch (const std::string& exception) { std::cout << "Exception: " << exception << std::endl; } return 0; } ``` 这里我们使用模板类`CXMUVector`来实现一个动态数组容器。其,`capacity`表示容器的容量,`size`表示容器的元素个数,`pdata`是指向动态数组存储位置的指针。在构造函数,我们进行了存储空间的分配,并在析构函数进行回收。 为了实现深度复制,我们重载了拷贝构造函数和赋值运算符。其,拷贝构造函数会创建一个新的动态数组,并将原数组的元素复制到新数组赋值运算符会首先删除原数组,然后创建一个新的动态数组,并将赋值对象的元素复制到新数组。 通过重载运算符`[]`,我们可以实现对容器元素的随机访问。如果索引超出容器大小的范围,将抛出`std::out_of_range`异常。 通过重载运算符`=`, 我们可以实现容器对象的深度赋值。在赋值之前,我们会首先删除原数组,然后创建一个新的动态数组,并将赋值对象的元素复制到新数组。 `PushBack`函数用于在向量末尾插入元素。如果存储空间不足,我们会将容量翻倍,并将原有元素复制到新的存储空间。 `PopBack`函数用于弹出向量末尾的元素,并返回该元素的值。如果向量为空,则抛出自定义异常字符串`"Pop On Empty Vector!"`。 在主函数,我们进行了一些测试。首先,我们创建一个`CXMUVector`对象,并使用`PushBack`函数插入一些元素。然后,我们使用重载运算符`[]`随机访问元素,并使用`PopBack`函数弹出末尾的元素。最后,我们测试了异常捕捉,进行了一次在空向量上的`PopBack`操作,期望抛出异常并捕获。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值