当前位置: 首页 > news >正文

C++ 拷贝赋值、swap 与 noexcept 深度解析:高效实现 operator=

        在 C++ 自定义类中,常常需要实现拷贝构造、赋值运算符、析构函数,其中,operator=(赋值运算符)处理不当,容易导致内存泄露、浅拷贝错误或性能浪费。

        本文通过一个简化的动态数组类 MyArray,一步步分析如何编写安全、高效、现代化的赋值运算符,并解释相关关键词如 std::copy、值传递 vs 引用传递、swap 技巧和 noexcept 优化等。

一、传统的赋值运算符实现方式

MyArray<T>& MyArray<T>::operator=(const MyArray<T>& arr) {if (this == &arr) return *this; // 防止自赋值if (this->pAddress != nullptr) {delete[] this->pAddress;this->pAddress = nullptr;}this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;this->pAddress = new T[m_Capacity];std::copy(arr.pAddress, arr.pAddress + m_Size, this->pAddress);return *this;
}

优点:

  • 支持自赋值检测

  • 深拷贝逻辑安全

缺点:

  • 重复代码较多

  • 写法不够现代

  • 容易出错,性能不高

现代推荐写法:Copy-and-Swap 技巧

MyArray& operator=(MyArray other) {swap(*this, other); // 利用参数副本直接交换std::cout << "拷贝/赋值 operator= 调用" << std::endl;return *this;
}

搭配一个全局友元函数 swap

friend void swap(MyArray& a, MyArray& b) noexcept {using std::swap;swap(a.pAddress, b.pAddress);swap(a.m_Capacity, b.m_Capacity);swap(a.m_Size, b.m_Size);
}

优点:

  • 安全:借助值传递(拷贝构造),出错不影响原对象

  • 简洁:释放、分配资源交给 swap 管

  • 可与移动构造结合,效率更高

二、为什么值传递(而不是引用)?

MyArray& operator=(MyArray other); // 传值

好处:

        自然实现自我保护,不需要显式处理自赋值 if (this == &other)

        结合移动构造,右值传参效率更高:

MyArray arr2 = std::move(arr1); 会触发移动构造 + swap,避免深拷贝

        语义简洁,无需再判断是否释放旧资源,一次 swap 解决所有权交接

但也要注意:

        如果传入的是左值,确实会生成一份副本,可能引发性能浪费(尤其数组很大时)。

std::copy 与 for 循环对比

std::copy(arr.pAddress, arr.pAddress + m_Size, this->pAddress);

等价于:

for (int i = 0; i < m_Size; ++i) {this->pAddress[i] = arr.pAddress[i];
}

优点:

  • 语义清晰

  • 简洁,易读

  • 编译器可对其优化(比如使用 SIMD 指令)

noexcept 的作用

friend void swap(MyArray& a, MyArray& b) noexcept

noexcept 说明该函数不会抛出异常

优点:

  1. 提高性能

    编译器可跳过异常处理路径
  2. STL 容器兼容性更好

    std::vector 只有在你的移动构造/赋值是 noexcept 才会使用它们
  3. 更强的异常安全保证

    防止程序在意外抛异常时崩溃

三、完整代码示意

template<class T>
class MyArray
{
public://如果你不传入参数,默认就使用 capacity = 0MyArray(std::size_t capacity = 0):pAddress(capacity ? new T[capacity] : nullptr),m_Capacity(capacity),m_Size(0) {std::cout << "有参构造函数调用" << std::endl;}//拷贝构造MyArray(const MyArray& arr):pAddress(arr.m_Capacity ? new T[arr.m_Capacity] : nullptr),m_Capacity(arr.m_Capacity),m_Size(arr.m_Size){std::cout << "拷贝构造调用" << std::endl;std::copy(arr.pAddress, arr.pAddress + arr.m_Size, pAddress);}//移动构造MyArray(MyArray&& arr)noexcept:pAddress(arr.pAddress),m_Capacity(arr.m_Capacity),m_Size(arr.m_Size){std::cout << "移动构造函数调用" << std::endl;arr.pAddress = nullptr;arr.m_Capacity = 0;arr.m_Size = 0;}//operator=MyArray& operator=(MyArray arr){swap(*this, arr);std::cout << "operator= 调用" << std::endl;return *this;}friend void swap(MyArray& a, MyArray& b) noexcept{using std::swap;swap(a.pAddress, b.pAddress);swap(a.m_Capacity, b.m_Capacity);swap(a.m_Size, b.m_Size);}//析构函数~MyArray(){std::cout << "析构函数的调用" << std::endl;delete[]this->pAddress;this->pAddress = nullptr;}
private:T* pAddress;size_t m_Capacity;size_t m_Size;
};
http://www.dtcms.com/a/316794.html

相关文章:

  • 工业数采引擎-通信链路SOCKET
  • Python高级编程与实践:Python网络编程基础与实践
  • Linux的NFS与Autofs配置指南
  • mac安装pycharm
  • 2048小游戏
  • VUE2 学习笔记 合集
  • 如何在nuxt项目中使用axios进行网络请求?
  • 【STM32】HAL库中的实现(三):PWM(脉冲宽度调制)
  • C语言线程同步详解(互斥锁、信号量、条件变量和读写锁)
  • GPU 安装
  • 前端包管理器深度对比
  • 双模对讲机是什么意思?与模拟/数字对讲机有什么区别?
  • 使用 ECharts 实现小区住户数量统计柱状图
  • Leetcode-2080区间内查询数字的频率
  • Spring AI 系列之三十六 - Spring AI Alibaba-nl2sql
  • 02--类和对象
  • 大厂面试题
  • React在使用create-react-app创建项目慢的解决办法
  • TanStack React Query 完全指南:从0到精通
  • Flux.1系列模型解析--Flux.1 Tools
  • 【模电笔记】—— 直流稳压电源——整流、滤波电路
  • 无线网络扫描工具 ‌WifiInfoView‌
  • 高通平台Wi-Fi Display学习-- 调试 Wi-Fi Display 问题
  • 人工智能领域、图欧科技、IMYAI智能助手2024年全年历史更新大事件汇总
  • 人工智能领域、图欧科技、IMYAI智能助手2025年6月更新月报
  • RHCA05--进程管理与文件系统管理
  • 基于SpringBoot的青少年网络安全教育系统
  • C语言:20250805学习(文件预处理)
  • 系统集成项目管理工程师【第十一章 规划过程组】项目管理计划、范围管理与收集需求篇
  • VUE丢失long类型精度,使用 json-bigint 库解析大整数