C++赋值运算符重载

C++运算符重载与赋值运算符重载解析

1.运算符重载

1.1运算符重载的定义

运算符的定义要用到一个关键字operator,那么这个定义的格式就是operator+运算符号+(参数列表)+函数主体+返回值,如下图所示

1.2运算符重载的概念

我们要确定一个概念,就是对象之间是可以使用运算符的,但是当运算符用于对象之间时,会调用对应的运算符重载,如果没有用于对象之间的这个运算符重载,编译器就会报错。

1.3运算符重载参数个数问题

运算符重载参数个数可以参考运算符的参数个数,比如

==有两个参数,A==B,

++有一个参数,A++  

-有两个参数 A-B     :      在左边就给左边参数,在右边就给右边参数

1.4当运算符重载是成员函数

 假如我在类中加一个private

那我们只能将运算符重载归到类中才能对private进行访问 

为什么这样会报错呢?

因为C++在设计的时候,类中的成员函数第一个运算对象默认传给this指针,所以上面的D1可以不用写,编译器会将它传给this指针

但是我们也要注意了,在引用那个值的时候我们不能再用结构体变量名称引用了,我们需要用this指针去指向那个值

1.5重载运算符的优先级和结合性与对应运算符保持一致 

1.6不能通过连接语法中没有的符号来创建新的运算符

比如operator¥,“¥”这种的符号的运算符根本没有

1.7有些运算符不能进行重载

比如       .*       和      ::       和       sizeof        和        ?:       和         .

上面5个运算符不能进行重载

1.8重载运算符至少有一个类类型的参数

像如果我们定义一个这样的重载运算符,编译器就会报错 

1.9前置类型++和后置类型++的区分

当我们要定义前置类型++和后置类型++时,我们的重载运算符都是operator++

不能区分开来,那么C++就设计了区分的方式

如果是后置++那么就在设置重载时再加一个int形参

如果是前置++那么就不需要加int形参 

 2.赋值运算符重载

1.赋值运算符重载的应用

赋值重载运算符的符号是=

赋值运算符重载和拷贝函数的功能类似都是将一个对象的数据拷贝到另外一个数据、

区别是赋值运算符重载是对两个已有的对象进行拷贝

拷贝函数是用一个已有的对象去初始化另外一个对象

建议将赋值运算重载函数的被拷贝参数加const,以防这个参数被修改

还有就是将赋值运算重载函数的返回值定义为类类型,方便进行多重赋值   

 

 2.赋值重载运算符的一些性质

当我们不去定义赋值重载运算符时,编译器会自动生成一个默认的赋值重载运算符,

这个默认的赋值重载运算符的拷贝是一个字节一个字节去拷贝的

所以对于类里面全是内置类型的对象,默认赋值重载运算符就可以解决问题,对于自定义类型,当要进行赋值时,编译器会自动调用它自己的赋值重载运算符。

但是当对象中存在向系统申请资源时,默认的赋值重载就不太能解决问题了,我们来测试一下

看上面代码我们来测试一下如下图 

你们看d2和d3的所有的变量都一致

但是代码运行结束的结果却是

造成这种现象的原因是因为d2和d3的arr指针都指向同一块区域,当d3结束调用析构函数时便会释放这块区域的数据, 然后d2结束调用它的析构函数时又会对这块区域释放数据,那么这块数据就释放了两次,所以当我们的内置类型有对系统申请资源的情况我们就要自己定义赋值重载运算符,

那么我们就要用到memcpy这个函数

如下代码

#include<iostream>
using namespace std;
class stack
{
public:
	stack& operator=(const stack& S1)
	{
		memcpy(this->arr, &S1, sizeof(int) * capacity);
		this->capacity = S1.capacity;
		this->top = 0;
		return *this;
	}
	stack(int n=10)
	{
		arr = (int*)malloc(sizeof(int) * n);
		if (arr == NULL)
		{
			perror("malloc");
			return;
		}
		capacity = 10;
		top = 0;
	}
	~stack()
	{
		free(arr);
		capacity = 0;
		top = 0;
	}
private:
	int* arr;
	int capacity;
	int top;
};
int main() {
	stack d2;
	stack d3;
	d3 = d2;
	return 0;
}

这种方式的赋值重载运算符就不会发生错误 

总结一下,当对象中没有对系统进行资源申请,那么编译器自动生成的赋值重载运算符就够用,如果有对系统进行资源申请,那么就需要我们自己定义赋值重载运算符。

<think>我们正在讨论C++中的赋值运算符重载,特别是与深拷贝和浅拷贝相关的内容。用户可能希望了解如何正确重载赋值运算符,避免浅拷贝带来的问题(如双重释放、内存泄漏等),并理解深拷贝与浅拷贝的区别。同时,用户引用的资料中提到了拷贝构造函数和禁止拷贝的方法,这些内容与赋值运算符重载紧密相关。回答结构:1.赋值运算符重载的基本概念和作用2.深拷贝与浅拷贝的区别及问题3.赋值运算符重载的典型写法(包括处理自赋值和释放原有资源)4.拷贝构造函数和赋值运算符的协同(拷贝交换技法)5.何时需要自定义赋值运算符重载6.如何禁止拷贝和赋值(使用delete)7.代码示例8.注意事项9.相关问题注意:根据用户提供的引用,引用[2]展示了深拷贝的拷贝构造函数,引用[3]展示了如何禁止拷贝,引用[4]说明了赋值运算符重载必须是成员函数以及默认行为(浅拷贝)。下面开始回答:</think> ### C++赋值运算符重载(Operator Overloading)使用指南 赋值运算符重载(`operator=`)是控制类对象拷贝行为的核心机制。正确实现可避免**浅拷贝**导致的资源泄漏或数据损坏,尤其涉及动态内存管理时[^1][^4]。 --- #### 一、核心概念 | **类型** | **特点** | **风险** | |----------------|--------------------------------------------------------------------------|------------------------------| | **浅拷贝** | 逐字节复制指针地址(编译器默认生成) | 多个对象共享资源 → 双重释放崩溃 | | **深拷贝** | 创建资源的新副本(需手动实现) | 无共享资源 → 安全但消耗更大 | | **禁止拷贝** | 用 `= delete` 显式删除拷贝操作 | 完全阻止拷贝行为 | --- #### 二、赋值运算符重载规范写法 ##### 1. 基本结构 ```cpp class MyClass { public: // 深拷贝赋值运算符 MyClass& operator=(const MyClass& other) { if (this != &other) { // 关键:检查自赋值! // 1. 释放原有资源 delete[] data; // 2. 深拷贝新资源 data = new int[other.size]; size = other.size; std::copy(other.data, other.data + size, data); } return *this; // 支持链式赋值 (a=b=c) } private: int* data; size_t size; }; ``` ##### 2. 处理自赋值与异常安全 ```cpp MyClass& operator=(const MyClass& other) { if (this != &other) { int* new_data = new int[other.size]; // 先创建新资源 std::copy(other.data, other.data + other.size, new_data); delete[] data; // 再释放旧资源 (异常安全) data = new_data; // 无异常再赋值 size = other.size; } return *this; } ``` > **为什么检查自赋值?** > 防止 `a=a` 时误释放自身资源 → 访问已释放内存崩溃[^4]。 --- #### 三、深度拷贝完整示例 ```cpp #include <iostream> #include <algorithm> class Vector { public: // 构造函数 Vector(size_t n) : size(n), data(new int[n]{}) {} // 深拷贝赋值 Vector& operator=(const Vector& other) { if (this != &other) { int* new_data = new int[other.size]; std::copy(other.data, other.data + other.size, new_data); delete[] data; data = new_data; size = other.size; } return *this; } // 析构函数 ~Vector() { delete[] data; } void print() { for (size_t i = 0; i < size; ++i) std::cout << data[i] << " "; std::cout << "\n"; } private: int* data; size_t size; }; int main() { Vector v1(3); // 初始化为 [0,0,0] Vector v2(2); // 初始化为 [0,0] v1 = v2; // 深拷贝赋值 v2 = v2; // 自赋值安全 } ``` --- #### 四、禁止拷贝的实现 当类不应被拷贝时(如管理唯一资源),显式删除拷贝操作: ```cpp class UniqueResource { public: UniqueResource() = default; // 删除拷贝构造和赋值 UniqueResource(const UniqueResource&) = delete; // [^3] UniqueResource& operator=(const UniqueResource&) = delete; // [^3] // 允许移动语义 UniqueResource(UniqueResource&&) noexcept = default; }; ``` --- #### 五、关键注意事项 1. **返回类型**:必须返回 `Class&`(支持链式赋值 `a=b=c`)[^4] 2. **自赋值检查**:避免 `if (this == &other) return *this;` 逻辑错误 3. **异常安全**:先分配新资源再释放旧资源 4. **协同拷贝构造**:深拷贝类需同时定义 `拷贝构造函数` 和 `operator=`[^2] 5. **移动语义**:C++11+ 中优先实现移动赋值 (`operator=&&`) 提升性能 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值