【C++】深拷贝浅拷贝

news2024/11/23 20:10:20

前言:在 C++ 中,当一个对象通过赋值或拷贝即将成为另一个对象的副本时,会发生“浅拷贝”或“深拷贝”。

什么是浅拷贝?

        浅拷贝是指两个对象共享同一个内存地址,即将源对象的数据成员的地址赋给了目标对象,当源对象和目标对象被销毁时,会发生重复释放的问题,导致程序崩溃。

        同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝.

        一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题。

 看图理解:

下面是一个浅拷贝的示例:

class Person {
public:
    int mAge;
    char* pName;
    Person(const char* n, int a) : name(new char[strlen(n) + 1]), mAge(a) {
        strcpy(pName, n);
    }

    // 浅拷贝构造函数
    Person(const Person & p) : age(p.mAge), name(p.pName) {} 
};

        在上述代码中,我们定义了一个 Person 类,包含了一个 char 类型的指针成员 pName 和一个 int 类型的成员 mAge。 在浅拷贝的构造函数中,会将源对象的地址直接赋值给目标对象,这样就会导致两个对象共享同一块内存,当其中一个对象被销毁时,会导致另一个对象的指针成员变量指向一块已被释放的内存,从而引发程序崩溃。

什么是深拷贝?

        深拷贝是指在拷贝对象时,会开辟一块新的内存空间来存储数据,并将源对象的数据成员的内容复制到目标对象的数据成员中,这样就不会有重复释放的问题。

        当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝

看图理解:

下面是一个深拷贝的示例:

class Person {
public:
    int mAge;
    char* pName;
    Person(const char* n, int a) : name(new char[strlen(n) + 1]), mAge(a) {
        strcpy(pName, n);
    }

    // 深拷贝构造函数
    Person(const Person & p) : age(p.mAge), name(new char[strlen(p.pName) + 1]) {
        strcpy(name, p.pName);
    } 
};

        在上述代码中,我们使用深拷贝的方式创建一个对象,将源对象的数据成员的内容复制到了目标对象中,防止了重复释放的问题。

如何选择?

        当对象中只包含了基本数据类型成员时,可以使用浅拷贝;当对象中包含了指针类型成员时,需要使用深拷贝。

        在实践中,我们需要根据具体的情况选择不同的拷贝方式。当对象中存在指针类型成员时,需要确保在拷贝时不会共享同一个地址,否则需要使用深拷贝。此外,在使用深拷贝时,还要注意好释放内存,避免内存泄漏的问题。

        下面以一个包含指针类型成员变量的例子来作为说明:

class Employee {
public:
    Employee(const char* n, int a, const char* p) : name(new char[strlen(n) + 1]), age(a), position(new char[strlen(p) + 1]) {
        strcpy(name, n);
        strcpy(position, p);
    }

    ~Employee() {
        delete[] name;
        delete[] position;
    }

    // 浅拷贝构造函数
    Employee(const Employee& other) : name(other.name), age(other.age), position(other.position) {} 

    // 深拷贝构造函数
    Employee(const Employee& other) : age(other.age), name(new char[strlen(other.name) + 1]), position(new char[strlen(other.position) + 1]) {
        strcpy(name, other.name);
        strcpy(position, other.position);
    }

private:
    char* name;
    int age;
    char* position;
};

        在上述代码中,我们定义了 Employee 类,其中包含了三个成员变量,包括两个 char 类型的指针类型成员变量 name 和 position,以及一个 int 类型的基本数据类型成员变量 age

        我们可以看到,如果要进行浅拷贝,我们只需要将源对象的指针类型成员变量的地址复制给目标对象的指针类型成员变量即可,但是这样做有一个问题就是源对象和目标对象会共享指针指向的内存空间,当源对象被销毁时,目标对象持有的指针就会指向一块已经被释放的内存空间,从而导致程序崩溃,因此浅拷贝对这种类型的成员变量并不适用。

        对于包含指针类型成员变量的对象,常用的拷贝方式是深拷贝。这种拷贝方式会为目标对象重新分配一块内存,并将源对象的数据成员的内容复制到目标对象的数据成员中,从而避免了重复释放的问题。在上述代码中,我们使用了深拷贝的方式来创建目标对象。

总结:

        在 C++ 的开发中,深拷贝和浅拷贝都是很重要的概念,具体应该根据对象的成员变量类型来选择拷贝方式,并且在使用深拷贝时需要注意好内存的释放问题。建议当对象中包含了指针类型成员时,使用深拷贝;当对象中只包含了基本数据类型成员时,可以使用浅拷贝。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/609447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue diff算法与虚拟dom知识整理(15) 终结篇,收尾新前到旧前全部不匹配情况

我们现在就只需要处理最后一种情况了 我们在 updateChildren.js 在while中 的if最后加个 else 当他们都没哟匹配到的情况 我们现在在updateChildren.js最上面 定义一个空对象 叫 keyMap 参考代码如下 let keyMap null;然后 在我们刚写的else中编写代码如下 //判断 如果…

Stable-Diffusion|入门怎么下载与使用civitai网站的模型(二)

C站:https://civitai.com/ 文章目录 1 样图2 实现2.1 下载主模型并放到正确文件夹中2.2 找到prompt2.3 生成2.4 Lora使用 3 一些有趣的项目3.1 胶片风格Lora3.2 [最近很火] 现实感很强的Majicmix-realistic 先贴几张笔者自己实验的图,模型来自&#xff1…

Libevent学习

一、Libevent概述 1、简介 Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大&…

C++必背基础知识点总结

重点:不借助任何外部资料就能掌握,考试面试大概率涉及。 掌握:不借助任何外部资料就能掌握,考试面试有可能涉及。 熟悉:可以适当参考资料掌握,考试面试有可能涉及。 了解:可是参考资料掌握&…

iptables的四表五链

文章目录 1. Iptables的链2.Iptables的表3.数据包过滤的匹配流程3.1 规则表之间的顺序3.2 规则链之间的顺序3.3 规则链内部各条防火墙规则之间的顺序3.4如何应用 每个 规则表,其实就相当于一个内核空间的容器, 按照规则集的不同用途进行划分为默认的四…

w11+wsl+3060安装cuda等深度学习环境

把一切都重新又梳理学习了一遍,然后发现很多事情其实没必要弄 0. 显卡驱动、nvidia-smi、cuda、nvcc、cuDNN、pytorch、cudatoolkit与它们之间的关系 笔者本人鸟枪换炮了,还是wsl环境香,但是按照官方教程跑完后,遇到了bugRuntim…

RTMPose关键点检测实战——笔记3

文章目录 摘要安装MMPose安装虚拟环境安装pytorch安装MMCV安装其他的安装包下载 MMPose下载预训练模型权重文件和视频素材 安装MMDetection安装Pytorch安装MMCV安装其它工具包下载 MMDetection安装MMDetection下载预训练模型权重文件和视频素材 MMPose预训练模型预测命令行的方…

关于render: h => h(App)的解释

当我们第一次安装完脚手架,打开 的时候,我相信,一定有小伙伴和我一样,看到main.js里面的render: h > h(App),感觉懵懵的。 因为,在刚开始接触vue的时候,我们这里是这样写的: 而使用了脚手…

iOS性能优化-异步绘制与异步底层View处理

前言: 基于UIKit的性能优化似乎已经到了瓶颈,无论是使用frame代理snpakit,缓存高度,减少布局层次,diff刷新,压缩图片,选择合适队列,选择高性能锁,也不能满足当前庞大而又…

SpringBoot 整合 MongoDB 实现数据的增删改查功能

1、介绍说明 在 MongoDB 中有三个比较重要的名词:数据库、集合、文档 数据库(Database):和关系型数据库一样,每个数据库中有自己的用户权限,不同的项目组可以使用不同的数据库 集合(Collectio…

汽车新能源 - 单体电压值为什么通常是5V以内

常见蓄电池单体电压的值(25℃),如下表: 蓄电池类型单体电压(V)铅酸 蓄电池2.08镍金属氢 蓄电池(NIMH)1.32锂离子 蓄电池2.5~4.2(典型3.6) 单体电压为什么不…

安卓学习笔记(一)

从今天开始我们开始学习安卓的知识: 1.0 Android基础入门教程 1.Android背景与当前的状况 Android系统是由Andy Rubin创建的,后来被Google收购了;最早的版本是:Android 1.1版本 而现在最新的版本是今年5.28,Google I/O大会上推…

Logstash部署与使用

ElasticSearch 1、ElasticSearch学习随笔之基础介绍 2、ElasticSearch学习随笔之简单操作 3、ElasticSearch学习随笔之java api 操作 4、ElasticSearch学习随笔之SpringBoot Starter 操作 5、ElasticSearch学习随笔之嵌套操作 6、ElasticSearch学习随笔之分词算法 7、ElasticS…

123网盘在线解析PHP版源码

123网盘解析PHP版本源码是一种非常实用的工具,可以方便地帮助用户在网页上直接解析出其它网站中的资源,并提供下载链接。当用户需要获取某些资源时,往往需要通过各种搜索引擎或者专业的资源网站进行查找,而且很多时候找到了资源链…

Android NDK集成OpenCV使用C++的.h和.so库函数

Android NDK集成OpenCV使用C的.h和.so库函数 opencv可以作为一个单独的Android module库,被工程下的其他模块使用,但是这样就没法在Android NDK项目的c代码直接使用opencv的.h文件和.so文件。要在Android NDK项目C代码文件中使用,则需要以An…

Nautilus Chain上首个DEX PoseiSwap即将开启IDO

据悉,Nautilus Chain 上的首个 DEX PoseiSwap 即将开启 IDO ,根据官方的最新公告显示,PoseiSwap 即将于 6 月 13 日至 6 月 14 日期间,在 Bounce 平台开启其治理通证 $POSE 的 IDO(Initial DEX Offering)&a…

数据分析--Numpy初级(一)

Numpy初级 Ndarray对象dtype对象 Numpy是数据分析的基础库,它支持大量的维度计算与矩阵运算。同时他也是一个运行速度非常快的数学库,主要用于数组计算,具有线性代数、傅里叶变换、随机数生成等功能。 Ndarray对象 Numpy最重要的一个特点就…

【内部类】

目录 1.什么是内部类2.内部类分类2.1静态内部类2.非静态内部类2.3 局部内部类(几乎不用,大家了解) 3.匿名内部类 1.什么是内部类 在Java中,可以将一个类定义在一个类中或者在一个方法中,前者称为内部类,后…

JAVA打印Hello World的底层实现

任何一个学过JAVA的人应该都对这段代码非常熟悉。空闲时间翻了下代码,看看它的底层是怎么实现的 public class HelloWorld {public static void main(String[] args) {System.out.print("Hello, World!");} }首先点开out,发现它是System类中的…

JAVA微服务_网关

服务网关 什么是服务网关/API网关 API Gateway(APIGW / API 网关),顾名思义,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供定制的API。 近几年来移动应用与企业间互联需求的兴起。从以前单一的…