11.一个对象=另一个对象会发生什么(赋值构造函数)

🔬 对象赋值的内部机制:赋值运算符解析

📖 内容概览

  • 理解C++中对象赋值操作的本质
  • 区分拷贝构造函数和拷贝赋值运算符
  • 掌握浅拷贝与深拷贝的区别及应用场景
  • 学习赋值运算符的实现要点和最佳实践

🎯 核心概念

🧩 赋值操作的本质

当执行对象A = 对象B时,实际上调用的是拷贝赋值运算符(而非构造函数)。如果类没有显式定义该运算符,编译器会自动生成合成的拷贝赋值运算符

🔄 浅拷贝与深拷贝

  • 浅拷贝:直接复制对象的所有成员值,包括指针地址。对于包含指针成员的类,会导致多个对象共享同一内存。
  • 深拷贝:为指针成员重新分配内存,并复制内存中的内容。每个对象拥有独立的内存空间。

📋 合成的拷贝赋值运算符

  • 编译器自动生成的拷贝赋值运算符执行浅拷贝
  • 对于无指针成员的类,浅拷贝通常是安全的
  • 对于有指针成员的类,浅拷贝会导致悬挂指针和多次释放内存的问题

🛠️ 赋值运算符的实现

🔧 基本语法

// 声明
Human& operator=(const Human& other);
// 实现
Human& Human::operator=(const Human& other) {
// 自赋值检查
if (this == &other) {
return *this; // 自赋值时直接返回
}
// 拷贝成员变量
this->name = other.name;
this->age = other.age;
this->sex = other.sex;
// 拷贝指针成员(浅拷贝)
strcpy_s(this->addr, ADDR_LEN, other.addr);
// 返回*this,支持链式赋值
return *this;
}

🔧 调用时机

// 对象赋值时自动调用
Human zhangsan("张三", 18, "男");
Human lisi;
lisi = zhangsan; // 调用拷贝赋值运算符

💻 完整代码示例

📝 Human.h

#pragma once
#include <string>
#include <iostream>
#include <Windows.h>
using namespace std;
class Human {
public:
Human();
~Human();
Human(string name, int age, string sex);
// 拷贝赋值运算符(注意:不是赋值构造函数)
Human& operator=(const Human& other);
// 成员函数
string getName() const;
string getSex() const;
int getAge() const;
const char* getAddr() const;
void setAddr(char* addr);
void description() const;
private:
string name; // 姓名(string自动管理内存)
string sex; // 性别(string自动管理内存)
int age; // 年龄(基本类型)
char* addr; // 地址(指针,需要手动管理内存)
};

📝 Human.cpp

#include "Human.h"
#define ADDR_LEN 64
Human::Human() {
name = "无名";
sex = "未知";
age = 18;
const char* addr_s = "China";
addr = new char[ADDR_LEN];
strcpy_s(addr, ADDR_LEN, addr_s);
}
Human::Human(string name, int age, string sex) {
this->age = age;
this->name = name;
this->sex = sex;
}
Human& Human::operator=(const Human& other) {
// 自赋值检查
if (this == &other) {
return *this;
}
// 拷贝非指针成员
this->name = other.name;
this->age = other.age;
this->sex = other.sex;
// 拷贝指针成员(浅拷贝)
strcpy_s(this->addr, ADDR_LEN, other.addr);
// 返回*this,支持链式赋值
return *this;
}
// 成员函数实现
string Human::getName() const { return name; }
string Human::getSex() const { return sex; }
int Human::getAge() const { return age; }
const char* Human::getAddr() const {
return addr;
}
void Human::setAddr(char* addr) {
if (!addr) return;
strcpy_s(this->addr, ADDR_LEN, addr);
}
Human::~Human() {
delete[] addr; // 析构函数释放动态内存
}
void Human::description() const {
cout << "姓名: " << name << ", 年龄: " << age << ", 性别: " << sex << endl;
}

main.cpp

#include "Human.h"
void showMsg(const Human& man1, const Human& man2) {
cout << "张三地址: " << man1.getAddr() << endl;
cout << "李四地址: " << man2.getAddr() << endl;
cout << "------------------------" << endl;
}
int main(void) {
Human zhangsan("张三", 18, "男");
Human lisi;
cout << "=== 初始化状态 ===" << endl;
zhangsan.description();
lisi.description();
// 调用拷贝赋值运算符
lisi = zhangsan;
cout << "=== 执行 lisi = zhangsan 后 ===" << endl;
showMsg(zhangsan, lisi);
// 修改张三的地址
zhangsan.setAddr((char*)"新加坡");
cout << "=== 张三修改地址后 ===" << endl;
showMsg(zhangsan, lisi);
system("pause");
return 0;
}

📊 执行结果分析

=== 初始化状态 ===
姓名: 张三, 年龄: 18, 性别: 男
姓名: 无名, 年龄: 18, 性别: 未知
------------------------
=== 执行 lisi = zhangsan 后 ===
张三地址: China
李四地址: China
=== 张三修改地址后 ===
张三地址: 新加坡

⚠️ 代码问题与优化

现有代码的问题

  1. 注释错误:将”拷贝赋值运算符”错误地称为”赋值构造函数”
  2. 浅拷贝风险:对于指针成员addr,当前实现是浅拷贝,可能导致多个对象共享同一内存
  3. 缺少内存释放:在赋值运算符中没有释放当前对象的旧内存

优化建议

Human& Human::operator=(const Human& other) {
// 1. 自赋值检查
if (this == &other) {
return *this;
}
// 2. 释放当前对象的旧内存
delete[] this->addr;
// 3. 重新分配内存
this->addr = new char[ADDR_LEN];
// 4. 拷贝数据
this->name = other.name;
this->age = other.age;
this->sex = other.sex;
strcpy_s(this->addr, ADDR_LEN, other.addr);
// 5. 返回*this
return *this;
}

💡 实现要点总结

  1. 自赋值检查:避免自赋值时的错误操作
  2. 资源管理:先释放旧资源,再分配新资源
  3. 深拷贝:对于指针成员,必须进行深拷贝
  4. 返回引用:支持链式赋值(如a = b = c
  5. 异常安全:确保在异常发生时,对象状态依然有效

🎯 核心概念回顾

概念关键点
拷贝赋值运算符用于已有对象间的赋值,语法:Class& operator=(const Class& other)
浅拷贝直接复制指针值,多个对象共享同一内存,不安全
深拷贝重新分配内存并复制内容,每个对象拥有独立内存,安全
自赋值检查避免自赋值时的资源释放错误
返回引用支持链式赋值,提高性能
理解拷贝赋值运算符的工作原理,是编写安全、高效C++代码的重要基础。通过正确实现赋值运算符,可以避免内存泄漏、悬挂指针等常见问题,提高程序的可靠性和稳定性。

Thanks for reading!

11.一个对象=另一个对象会发生什么(赋值构造函数)

2026-01-23
1403 字 · 7 分钟

已复制链接

评论区

目录