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

c++面向对象第4天---拷贝构造函数与深复制

  • 含有对象成员的构造函数
  • 深复制与浅复制
  • 拷贝(复制)构造函数

第一部分:含有对象成员的构造函数

以下是一个学生 类包含日期成员出生日期的代码

#include<iostream>
using namespace std;
class Date
{
public:Date(int year,int month,int day):year(year),month(month),day(day){cout << "Date 构造函数被调用" << endl;}
void printDate()
{cout << year << "年" << month << "月" << day << "日";
}private:
int year,month,day;};class Student
{
public:Student(string name,Date birthday) : name(name),birthday(birthday){cout << "student 的构造函数被调用" << endl;}Student(string name,int year,int month,int day):name(name),birthday(year,month,day){cout << "Student 的构造函数被调用222" << endl;}void printStudentInfo(){cout << "姓名" << name << ",出生日期" << endl;birthday.printDate();cout << endl;}
private:string name;Date birthday;
};int main()
{Student stu1("小明",{2002,5,4}),stu2("小红",2003,3,6);stu1.printStudentInfo();stu2.printStudentInfo();return 0;
}

Date 构造函数被调用
student 的构造函数被调用
Date 构造函数被调用
Student 的构造函数被调用222
姓名小明,出生日期
2002年5月4日
姓名小红,出生日期
2003年3月6日

小结:含有对象成员的类创建对象时,对象成员的初始化要在初始化列表中,运行时会先调用对象成员的构造函数,再调用当前类自己的构造函数。

第2部分:深复制与浅复制

#include<iostream>
using namespace std;class Mystring
{
public:
Mystring(const char* str)
{length = strlen(str);data = new char[length + 1];strcpy(data,str);
}
~Mystring()
{delete[] data;
}
void printString()
{cout << data << endl;
}private:
char* data;
int length;
};int main()
{Mystring s("Hello world!");s.printString();Mystring s2 = s;s2.printString();return 0;
}

浅复制:浅复制是默认的构造函数

Mystring s2 = s;

没有写拷贝构造函数时,系统会自动生成一个“默认拷贝构造函数”,它会对每个成员变量做“逐成员复制”。

对于普通变量(如 int length)是没问题的,但对于指针(char* data),浅复制只是把地址复制过去,不会新建一份数据内容

 结果就是 s.data 和 s2.data 都指向同一块内存!

这就会导致:

  • 两个对象共享同一块堆内存

  • 当 s 和 s2 析构时,都执行 delete[] data,会对同一内存释放两次,造成程序崩溃或未定义行为(⚠️ 危险)

那么深复制的时候:就是复制内容,而不是复制指针地址,所以你只需要手动实现拷贝构造函数

Mystring(const Mystring& other)
{
    length = other.length;
    data = new char[length + 1];       // 分配新内存
    strcpy(data, other.data);          // 复制内容
}

 

所以这个就是以下的区别:

 

第三部分:拷贝(复制)构造函数

#include<iostream>
using namespace std;class Mystring
{
public:
Mystring(const char* str)
{length = strlen(str);data = new char[length+1];strcpy(data,str);cout << "Mystring的构造函数被调用" << endl;
}
Mystring(const Mystring& str)
{length = str.length;data = new char[length+1];strcpy(data,str.data);cout << "Mystring 的拷贝构造函数被调用" << endl;
}
~Mystring()
{delete[] data;
}
void printString()
{cout << data << endl;
}
private:
char* data;
int length;
};int main()
{Mystring s("Hello world");s.printString();Mystring s2(s);s2.printString();return 0;
}
  • 必须使用引用参数(避免无限递归调用)
  • 通常为 const 引用(保证源对象不被修改)
  • 编译器默认生成(仅当类无用户自定义构造函数时)

那这里面的拷贝构造函数是什么?

Mystring(const Mystring& str)
{length = str.length;data = new char[length + 1];      // 分配新空间strcpy(data, str.data);           // 拷贝字符串内容cout << "Mystring 的拷贝构造函数被调用" << endl;
}

 

用一个已经存在的对象去创建一个新的对象,并且为其分配新的内存( 深拷贝),防止多个对象共享同一块内存。

 Mystring s2(s);

调用拷贝构造函数 Mystring(const Mystring& str) 创建新对象 s2

#include<iostream>
#include<string>
using namespace std;class CPU{
public:CPU(const string& cpu) : model(cpu){cout << "cpu constructed" << endl;}void printInfo() const{cout << "CPU型号" << model << endl;}
private:string model;
};class Monitor{
public:Monitor(const string& monitor) : brand(monitor){cout << "Monitor constructed" << endl;}void printInfo() const{cout << "Monitor品牌" << brand << endl;}
private:string brand;
};class Computer{
public:Computer(int memory,const string& cpuModel,const string& monitorBrand): memory(memory),cpu(cpuModel),monitor(monitorBrand){cout << "Computer构造函数被调用" << endl;}void printInfo() const{cout << "内存容量 " << memory << " GB" << endl;cpu.printInfo();monitor.printInfo();}
private:int memory;CPU cpu;Monitor monitor;
};int main()
{Computer c(16,"Intel 890000","mac");c.printInfo();return 0;
}

 

  1. 定义一个Person类,包含char*类型的name,int age和Adress类型的address。Address是表示地址的类,包含char* street和int类型的houseNumber。请合理实现这两个类,使其能正常的完成如下的使用:
  2. #include <iostream>
    #include <cstring>
    using namespace std;class Address {
    public:Address(const char* streetName, int houseNum) {street = new char[strlen(streetName) + 1];strcpy(street, streetName);houseNumber = houseNum;cout << "Address构造函数被调用" << endl;}Address(const Address& other) {street = new char[strlen(other.street) + 1];strcpy(street, other.street);houseNumber = other.houseNumber;}~Address() {delete[] street;}void printAddress() const {cout << "地址: " << street << ", 门牌号: " << houseNumber << endl;}private:char* street;int houseNumber;
    };class Person {
    public:Person(const char* n, int a, const Address& addr) : age(a), address(addr) {name = new char[strlen(n) + 1];strcpy(name, n);cout << "Person构造函数被调用" << endl;}Person(const Person& other) : age(other.age), address(other.address) {name = new char[strlen(other.name) + 1];strcpy(name, other.name);}~Person() {delete[] name;}void printInfo() const {cout << "姓名: " << name << ", 年龄: " << age << endl;address.printAddress();}private:char* name;int age;Address address;
    };int main() {Address addr("人民路", 100);Person p("张三", 20, addr);p.printInfo();return 0;
    }

相关文章:

c++面向对象第4天---拷贝构造函数与深复制

含有对象成员的构造函数深复制与浅复制拷贝&#xff08;复制&#xff09;构造函数 第一部分&#xff1a;含有对象成员的构造函数 以下是一个学生 类包含日期成员出生日期的代码 #include<iostream> using namespace std; class Date { public:Date(int year,int month…...

Windows版PostgreSQL 安装 vector 扩展

问题 spring-ai在集成PGVector向量存储的时候会报错如下&#xff0c;那么就需要安装pgsql的vector扩展。 SQL [CREATE EXTENSION IF NOT EXISTS vector]; 错误: 无法打开扩展控制文件 "C:/Program Files/PostgreSQL/9.6/share/extension/vector.control": No such …...

KINGCMS被入侵

现象会强制跳转到 一个异常网站,请掉截图代码. 代码中包含经过混淆处理的JavaScript&#xff0c;它使用了一种技术来隐藏其真实功能。代码中使用了eval函数来执行动态生成的代码&#xff0c;这是一种常见的技术&#xff0c;恶意脚本经常使用它来隐藏其真实目的。 这段脚本会检…...

完美解决在pycharm中创建Django项目安装mysqlclient报错的问题(windows下)

正常情况下&#xff0c;在Windows安装mysqlclient会报错&#xff1a; 我这里用的是anaconda虚拟环境&#xff0c;安装前必须激活anacoda虚拟环境&#xff0c; 怎么激活虚拟环境&#xff1f;可以参考超详细的pycharmanaconda搭建python虚拟环境_pycharm anaconda环境搭建-CSDN博…...

『React』组件副作用,useEffect讲解

在 React 开发中&#xff0c;有时候会听到“副作用”这个词。特别是用到 useEffect 这个 Hook 的时候&#xff0c;官方就明确说它是用来处理副作用的。那什么是副作用&#xff1f;为什么我们要专门管控它&#xff1f;今天就聊聊 React 中的组件副作用。 &#x1f4cc; 什么是“…...

使用VSCode在WSL和Docker中开发

通过WSL&#xff0c;开发人员可以安装 Linux 发行版&#xff08;例如 Ubuntu、OpenSUSE、Kali、Debian、Arch Linux 等&#xff09;&#xff0c;并直接在 Windows 上使用 Linux 应用程序、实用程序和 Bash 命令行工具&#xff0c;不用进行任何修改&#xff0c;也无需使用传统虚…...

ZooKeeper 命令操作

文章目录 Zookeeper 数据模型Zookeeper 服务端常用命令Zookeeper 客户端常用命令 Zookeeper 数据模型 ZooKeeper 是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似&#xff0c;拥有一个层次化结构。这里面的每一个节点都被称为&#xff1a; ZNode&#xff0c;每个节…...

解决 Ubuntu 20.04 虚拟机中 catkin_make 编译卡死问题

完整解决步骤 1. 禁用当前交换文件 sudo swapoff /swapfile 2. 删除旧的交换文件 sudo rm /swapfile 3. 使用更可靠的创建方法 # 使用 dd 命令创建交换文件&#xff08;更兼容但较慢&#xff09; sudo dd if/dev/zero of/swapfile bs1M count4096# 或者使用 truncate 命令…...

【HTML-15】HTML表单:构建交互式网页的基石

表单是HTML中最强大的功能之一&#xff0c;它允许网页收集用户输入并与服务器进行交互。无论是简单的搜索框、登录页面&#xff0c;还是复杂的多步骤调查问卷&#xff0c;表单都是实现这些功能的核心元素。本文将深入探讨HTML表单的各个方面&#xff0c;帮助您构建高效、用户友…...

一些较好的学习方法

1、网上有一些非常经典的电路&#xff0c;而且有很多视频博主做了详细的讲解。 2、有一部分拆解的UP主&#xff0c;拆解后会还原该器件的原理图&#xff0c;并一步步做讲解。 3、有两本书&#xff0c;数电、模电&#xff0c;这两本书中的内容很多都值得学习。 5、某宝上卖的…...

Redis底层数据结构之深入理解跳表(1)

在上一篇文章中我们详细的介绍了一下Redis中跳表的结构以及为什么Redis要引入跳表而不是平衡树或红黑树。这篇文章我们就来详细梳理一下跳表的增加、搜索和删除步骤。 SkipList的初始化 跳表初始化时&#xff0c;将每一层链表的头尾节点创建出来并使用集合将头尾节点进行存储&…...

鸿蒙【HarmonyOS 5】 (React Native)的实战教程

一、环境配置 ‌安装鸿蒙专属模板‌ bashCopy Code npx react-native0.72.5 init HarmonyApp --template react-native-template-harmony:ml-citation{ref"4,6" data"citationList"} ‌配置 ArkTS 模块路径‌ 在 entry/src/main/ets 目录下创建原生模块&…...

PCB设计教程【入门篇】——电路分析基础-元件数据手册

前言 本教程基于B站Expert电子实验室的PCB设计教学的整理&#xff0c;为个人学习记录&#xff0c;旨在帮助PCB设计新手入门。所有内容仅作学习交流使用&#xff0c;无任何商业目的。若涉及侵权&#xff0c;请随时联系&#xff0c;将会立即处理 目录 前言 一、数据手册的重要…...

20250529-C#知识:继承、密封类、密封方法、重写

C#知识&#xff1a;继承、密封类、密封方法、重写 继承是面向对象的三大特性之一&#xff0c;通过继承能够减少重复代码的编写&#xff0c;有助于提升开发效率。 1、继承 C#不同于C&#xff0c;只支持单继承当子类出现与父类同名的成员时&#xff0c;父类成员被隐藏&#xff0…...

从0到1,带你走进Flink的世界

目录 一、Flink 是什么&#xff1f; 二、Flink 能做什么&#xff1f; 三、Flink 架构全景概览 3.1 分层架构剖析 3.2 核心组件解析 四、Flink 的核心概念 4.1 数据流与数据集 4.2 转换操作 4.3 窗口 4.4 时间语义 4.5 状态与检查点 五、Flink 安装与快速上手 5.1 …...

springboot @value

#springboot value value 可以读取 yaml 中 的数据...

Dify-5:Web 前端架构

本文档提供了 Dify Web 前端架构的技术概述&#xff0c;包括核心组件、结构和关键技术。它解释了前端如何组织、组件如何通信以及国际化功能如何实现。 技术栈 Dify 的 Web 前端基于现代 JavaScript 技术栈构建&#xff1a; 框架&#xff1a;Next.js&#xff08;基于 React …...

深度学习赋能图像识别:技术、应用与展望

论文&#xff1a; 一、引言​ 1.1 研究背景与意义​ 在当今数字化时代&#xff0c;图像作为信息的重要载体&#xff0c;广泛存在于各个领域。图像识别技术旨在让计算机理解和识别图像内容&#xff0c;将图像中的对象、场景、行为等信息转化为计算机能够处理的符号或数据 &am…...

八N皇后问题

1 问题的提出 在8X8格的国际象棋上摆放八个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上&#xff0c;问有多少种摆法 我们的任务就是用MATLAB进行求解 2 数学模型的构建 首先我们分析题目就是 任意两个皇后都不能处于…...

TMS320F28388D使用sysconfig配置IPC

第1章 配置IPC底层代码 使用IPC的动机&#xff1a; 我计划我的项目中要使用RS485&#xff0c;CANFD通信和EtherCAT通信&#xff0c;由于通信种类较多&#xff0c;而对于电机控制来说大部分数据都是重复的&#xff0c;并且有些数据可以很久才改变一次&#xff0c;所以我计划使…...

代码训练LeetCode(19)轮转数组

代码训练(19)LeetCode之轮转数组 Author: Once Day Date: 2025年6月3日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09;力扣 (LeetCode) 全球极客挚爱的…...

每日算法 -【Swift 算法】将整数转换为罗马数字

&#x1f4a1; Swift&#xff1a;将整数转换为罗马数字&#xff08;含思路讲解与详细注释&#xff09; 罗马数字是一种古老的数字表示方式&#xff0c;虽然在现代我们不再使用它进行计算&#xff0c;但在表盘、章节、纪念碑等地方依然很常见。今天我们就来实现一个经典算法题&…...

Qwen与Llama分词器核心差异解析

Qwen和 Llama 词映射(分词器)的区别及通用词映射逻辑 一、Qwen 与 Llama 词映射(分词器)区别 维度Qwen 分词器Llama 分词器技术基础基于字节级别字节对编码(BBPE),以 cl100k 为基础词库,扩充中文字词、多语言词汇基于 BPE,但依赖 SentencePiece 单字模型,核心为英文优…...

华为云Flexus+DeepSeek征文 | 基于ModelArts Studio 与 Cline 快速构建AI编程助手

目录 一、前言 二、ModelArts Studio&#xff08;MaaS&#xff09;介绍与应用场景 2.1ModelArts Studio&#xff08;MaaS&#xff09;介绍 2.2 ModelArts Studio&#xff08;MaaS&#xff09;使用场景 2.3 开通MaaS服务 2.4 开通DeepSeek-V3商用服务 三、Cline简介和安装 3.1 C…...

pikachu靶场通关笔记11 XSS关卡07-XSS之关键字过滤绕过(三种方法渗透)

目录 一、源码分析 1、进入靶场 2、代码审计 3、攻击思路 二、渗透实战 1、探测过滤信息 2、注入Payload1 3、注入Payload2 4、注入Payload3 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#xff0c;通过对XSS关卡源码的代码审计找到安…...

Android App引用vendor编写的jni动态库

简单描述一下&#xff0c;就是我自己基于FastDDS写了一个Jni的so&#xff0c;然后编写了jar包引用该so&#xff0c;最后写了一个Android的测试apk使用jar包&#xff0c;调用jni中的接口去创建Participant&#xff0c;Subscriber等。 实际将jni的so放到 /system_ext/lib64&#…...

React从基础入门到高级实战:React 核心技术 - 错误处理与错误边界:构建稳定的应用

React 错误处理与错误边界&#xff1a;构建稳定的应用 在开发 React 应用时&#xff0c;错误处理是确保应用稳定性和用户体验的重要环节。无论是运行时错误、API 请求失败还是用户操作失误&#xff0c;合理的错误处理机制都能防止应用崩溃&#xff0c;并为用户提供清晰友好的反…...

页面输入数据的表格字段(如 Web 表单或表格控件)与后台数据库进行交互时常用的两种方式

“从页面输入数据的表格字段(如 Web 表单或表格控件)在与后台数据库进行交互时,常用的有两种方式:” 🎯 两种方式(操作调用数据库、绑定数据) 🚀 方式1:前端代码提交数据到后端,再由后端调用数据库 💡 原理和逻辑: 用户在页面上(比如输入表单、表格)输入数据…...

碰一碰发视频-源码系统开发技术分享

#碰一碰营销系统# #碰一碰系统# #碰一碰发视频# 架构设计哲学&#xff1a;近场通信的优雅平衡 一、核心通信技术选型 1. 双模协同传输引擎 技术协议栈延迟控制适用场景NFCISO 14443-A<100ms精准触发场景BLE 5.0GATT Profile300-500ms中距传输场景 工程决策依据&…...

C++学习过程分享

空指针&#xff1a;int *p NULL; 空指针&#xff1a;指针变量指向内存中编号为0的空间&#xff1b;用途&#xff1a;初始化指针变量注意&#xff1a;空指针指向的内存不允许访问注意&#xff1a;内存编号为0-255为系统占用空间&#xff0c;不允许用户访问 野指针&#xff1a;…...