C++ 赋值运算符重载

本文介绍了C++中赋值运算符重载的重要性,通过示例展示了浅拷贝导致的问题及如何通过实现深拷贝来解决。详细解释了在类中拥有动态分配内存的属性时,如何正确地重载赋值运算符以避免内存泄漏和数据错误。

C++ 赋值运算符重载

一、概念:
c++ 编译器至少给一个类添加4个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝
  4. 赋值运算符 operator=, 对属性进行值拷贝
    如果类中有属性指向堆区,做赋值操作时,也会出现深浅拷贝的问题

二、示例
1、浅拷贝

#include <iostream>
#include <string>

using namespace std;

class Persion
{
public:
	Persion(int age)
	{
		m_pAge = new int(age);
	}

	int *m_pAge;
};

void test01()
{
	Persion p1(18);
	cout << "p1的年龄为:" << *p1.m_pAge << endl;    // 18
	Persion p2(20);
	cout << "p2的年龄为:" << *p2.m_pAge << endl;    // 20
	
	p1 = p2;
	cout << "p1的年龄为:" << *p1.m_pAge << endl;    // 20
	cout << "p2的年龄为:" << *p2.m_pAge << endl;    // 20
}

int main()
{
	test01();
	system("pause");
	return 0;
}

释放内存会出问题:

#include <iostream>
#include <string>

using namespace std;

class Persion
{
public:
	Persion(int age)
	{
		m_pAge = new int(age);
	}
	// 加入析构函数   程序奔溃
	~Persion()
	{
		if (m_pAge)
		{
			delete m_pAge;
			m_pAge = nullptr;
		}
	}

	int *m_pAge;
};

void test01()
{
	Persion p1(18);
	cout << "p1的年龄为:" << *p1.m_pAge << endl;    // 18
	Persion p2(20);
	cout << "p2的年龄为:" << *p2.m_pAge << endl;    // 20
	
	p1 = p2;
	cout << "p1的年龄为:" << *p1.m_pAge << endl;    // 20
	cout << "p2的年龄为:" << *p2.m_pAge << endl;    // 20
}

int main()
{
	test01();
	system("pause");
	return 0;
}

浅拷贝会导致程序奔溃,原因是m_pAge 这个指针释放了2次,那么怎么解决呢?
使用深拷贝
在这里插入图片描述
在这里插入图片描述

示例:

#include <iostream>
#include <string>

using namespace std;

class Persion
{
public:
	Persion(int age)
	{
		m_pAge = new int(age);
	}

	~Persion()
	{
		if (m_pAge)
		{
			delete m_pAge;
			m_pAge = nullptr;
		}
	}
	Persion& operator=(const Persion& p)
	{
		// 编译器提供的是浅拷贝
		//m_pAge = p.m_pAge;

		// 我们要写深拷贝
		// 先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
		if (m_pAge)
		{
			delete m_pAge;
			m_pAge = nullptr;
		}
		// 深拷贝
		m_pAge = new int(*p.m_pAge);
		// 返回对象本身
		return *this;
	}

	int *m_pAge;
};

void test01()
{
	Persion p1(18);
	cout << "p1的年龄为:" << *p1.m_pAge << endl;    // 18
	Persion p2(20);
	cout << "p2的年龄为:" << *p2.m_pAge << endl;    // 20
	Persion p3(25);
	cout << "p3的年龄为:" << *p3.m_pAge << endl;    // 25
	
	p1 = p2 = p3;
	cout << "p1的年龄为:" << *p1.m_pAge << endl;    // 25
	cout << "p2的年龄为:" << *p2.m_pAge << endl;    // 25
	cout << "p3的年龄为:" << *p3.m_pAge << endl;    // 25
}

int main()
{
	test01();
	system("pause");
	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、付费专栏及课程。

余额充值