拷贝构造函数与深拷贝和浅拷贝

2023-11-13

拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。 作用就是用来复制对象,在使用这个对象的实例来初始化这个对象的一个新的实例。类中可以存在多个拷贝构造函数。

拷贝构造函数的调用时机

  • 当函数的参数为类的对象时
#include<iostream>
using namespace std;
class CExample
{
private:
    int a;
public:
    CExample(int b)
    {
        a=b;
        printf("constructor is called\n");
    }
    CExample(const CExample & c)
    {
        a=c.a;
        printf("copy constructor is called\n");
    }
    ~CExample()
    {
     cout<<"destructor is called\n";
    }
    void Show()
    {
     cout<<a<<endl;
    }
};
void g_fun(CExample c)
{
    cout<<"g_func"<<endl;
}
int main()
{
    CExample A(100);
    CExample B=A;
    B.Show(); 
    g_fun(A);
    return 0;
}

调用g_fun()时,会产生以下几个重要步骤:
(1).A对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把A的值给C。 整个这两个步骤有点像:CExample C(A);
(3).等g_fun()执行完后, 析构掉 C 对象。

  • 函数的返回值是类的对象
#include<iostream>
using namespace std;
class CExample
{
private:
    int a;
public:
    //构造函数
    CExample(int b)
    {
     a=b;
        printf("constructor is called\n");
    }
    //拷贝构造函数
    CExample(const CExample & c)
    {
     a=c.a;
        printf("copy constructor is called\n");
    }
    //析构函数
    ~CExample()
    {
     cout<<"destructor is called\n";
    }
    void Show()
    {
     cout<<a<<endl;
    }
};
CExample g_fun()
{
    CExample temp(0);
    return temp;
}
int main()
{
    
    g_fun();
    return 0;
}

当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_fun()执行完后再析构掉XXXX对象。

  • 对象需要通过另外一个对象进行初始化


CExample A(100);
CExample B=A;


浅拷贝与深拷贝

拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝,深拷贝会在堆内存中另外申请空间来储存数据。默认的拷贝构造函数实现的是浅拷贝,数据成员中有指针时,必须要用深拷贝。

一般的赋值操作是深度拷贝:

//深度拷贝
int a = 5;
int b = a;

简单的指针指向,则是浅拷贝:

//浅拷贝
int a = 8;
int *p;
p = &a;

char* str1 = "HelloWorld";
char* str2 = str1;

如果要将上面的浅拷贝转换为深拷贝,需要这样

//深度拷贝
int a = 8;
int *p = new int;
*p = a;

char* str1 = "HelloWorld";
int len = strlen(str1);
char *str2 = new char[len];
memcpy(str2, str1, len);

以字符串拷贝为例,浅拷贝后,str1和str2同指向0x123456,不管哪一个指针,对该空间内容的修改都会影响另一个指针。
在这里插入图片描述
深拷贝后,str1和str2指向不同的内存空间,各自的空间的内容一样。因为空间不同,所以不管哪一个指针,对该空间内容的修改都不会影响另一个指针。
在这里插入图片描述
当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。这时,必须采用深拷贝。

默认的拷贝构造函数存在弊端,看如下类定义

class TestCls{
public:
    int a;
    int *p;

public:
    TestCls()   //无参构造函数
    {
        std::cout<<"TestCls()"<<std::endl;
        p = new int;
    }

    ~TestCls()     //析构函数
    {
        delete p;   
        std::cout<<"~TestCls()"<<std::endl;
    }
};


int main(void)
{
    TestCls t1;
    TestCls t2 = t1;   //效果等同于TestCls t2(t1);

    return 0;
}

类的默认拷贝构造函数只会用被拷贝类的成员的值为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。以前面TestCls可以知道,编译器为我们默认定义的拷贝构造函数为:

TestCls(const TestCls& testCls)
{
    a = testCls.a;
    p = testCls.p;      //两个类的p指针指向的地址一致。
}

main函数将要退出时,拷贝类t2的析构函数先得到执行,它把自身p指向的堆空间释放了;接下来,t1的析构函数得到调用,被拷贝类t1的析构函数得到调用,它同样要去析构自身的p指向指向的堆空间,但是该空间和t2类中p指向的空间一样,造成重复释放,程序运行崩溃。

解决办法十分简单,自定义拷贝构造函数,里面用深度拷贝的方式为拷贝类初始化:

class TestCls{
public:
    int a;
    int *p;

public:
    TestCls()
    {
        std::cout<<"TestCls()"<<std::endl;
        p = new int;
    }


    TestCls(const TestCls& testCls)
    {
        std::cout<<"TestCls(const TestCls& testCls)"<<std::endl;
        a = testCls.a;
        //p = testCls.p;
        p = new int;

        *p = *(testCls.p);      //为拷贝类的p指针分配空间,实现深度拷贝
    }

    ~TestCls()
    {
        delete p;   
        std::cout<<"~TestCls()"<<std::endl;
    }
};

int main(void)
{
    TestCls t1;
    TestCls t2 = t1;

    return 0;
}

防止默认拷贝发生

自定义拷贝构造函数,并设置为private属性,其实现体可以什么都不写,那么这个类将变成一个不可被复制的类了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

拷贝构造函数与深拷贝和浅拷贝 的相关文章

  • 拷贝构造函数

    拷贝构造函数是重载构造函数的一种重要形式 xff0c 它的功能是使用一种已经存在的对象去初始一个新创建的同类对象 xff0c 它可以将一个已有对象的数据成员的值拷贝给正在创建的另一个同类的对象 拷贝构造函数与类同名 xff0c 没有返回值
  • C++big three(构造函数、拷贝构造函数,拷贝赋值函数)

    一个类中只要带有指针类型的成员 xff0c 就必须自己写出big three xff08 构造函数 拷贝构造函数 xff0c 拷贝赋值函数 xff09 xff0c 如果没有指针类型的成员 xff0c 大部分情况下可以用默认的 字符串类是一个
  • 深拷贝与浅拷贝的区别,实现深拷贝的几种方法

    如何区分深拷贝与浅拷贝 简单点来说 就是假设B复制了A 当修改A时 看B是否会发生变化 如果B也跟着变了 说明这是浅拷贝 拿人手短 如果B没变 那就是深拷贝 自食其力 此篇文章中也会简单阐述到栈堆 基本数据类型与引用数据类型 因为这些概念能
  • 什么时候要有拷贝构造函数?

    1 构造对象 时 用已实例化的对象进行构造 int main Student a 10 2 Student b a 拷贝构造 并且要特别注意指针数据成员的赋值 避免出现野指针的情况 2 返回临时对象 而不是返回对象的引用 rmb opera
  • OC中的copy和mutableCopy

    在OC中 对对象的拷贝可分为深拷贝和浅拷贝 深拷贝 会生成新的指针和新的内存 新的指针指向新开辟的内存空间 并且会将原来的内存中的内容也拷贝过来 浅拷贝 会生成新的指针 但是不会开辟新的内存空间 也不会拷贝原来内存中的内容 新生成的指针会指
  • 数组的浅拷贝与深拷贝

    文章目录 1 数据类型 2 浅拷贝与深拷贝 3 实现深拷贝方法 3 1 JSON string 结合 JSON parse 3 2 递归 4 JS 中的拷贝方法 4 1 concat 4 2 slice 4 3 展开运算符 4 4 Obje
  • 2022-12-27 使用lodash库实现两个非空对象的深拷贝并输出这两个对象的并集

    问题描述 遇到这样一个题 如下 const a fruits apple banana series apple C banana A B const b fruits banana orange animals pig series ba
  • 【深入理解C++】调用父类的拷贝构造函数

    文章目录 1 默认的拷贝操作 2 调用父类的拷贝构造函数 3 用子类对象初始化父类对象 1 默认的拷贝操作 默认情况下 继承体系下类对象的拷贝是每个成员变量逐个拷贝 include
  • Bean深拷贝忽略大小写

    import java lang reflect Field import java util HashMap import java util Map public class BeanMapUtils 忽略大小写且NULL值不会覆盖新值
  • 第二章 构造函数语意学 编译器何时合成拷贝构造函数?

    首先要清楚位拷贝 浅拷贝 和值拷贝 深拷贝 的区别 参考http blog sina com cn s blog a2aa00d70101gpvj html 位拷贝 及 bitwise copy 是指将一个对象的内存映像按位原封不动的复制给
  • c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起

    本文收录于微信公众号 LinuxOK ID为 Linux ok 关注公众号第一时间获取更多技术学习文章 1 c 类的默认拷贝构造函数的弊端 c 类的中有两个特殊的构造函数 1 无参构造函数 2 拷贝构造函数 它们的特殊之处在于 1 当类中没
  • 类模板以及赋值运算符重载、拷贝构造函数

    编译器默认的拷贝构造函数 是发生的浅拷贝 像指针的赋值就会让指针指向同一个地址空间 析构时就会对同一个地址空间释放两次 就会造成程序崩溃 自定义在模板内的拷贝构造函数 Queue const Queue
  • Vue - 使用Lodash进行深拷贝

    文章目录 深浅拷贝的理解 使用lodash 深浅拷贝的理解 浅拷贝 只是将数据中所有的数据引用下来 依旧指向同一个存放地址 拷贝之后的数据修改之后 也会影响到原数据的中的对象数据 例如 Object assign 扩展运算符 深拷贝 将数据
  • Python:赋值,浅拷贝(copy)和深拷贝(deepcopy)

    基础知识请查看之前博客 Python 对象 可变对象与不可变对象 赋值 浅拷贝和深拷贝的关键问题 修改一个变量 会不会导致另外拷贝出来的对象的改变 不可变对象 import copy a1 0 a2 a1 a3 copy copy a1 a
  • Java对象深拷贝的几种方式

    对象拷贝 项目开发过程中很多时候需要进行对象复制 可是有的时候会发生复制后的对象 在原对象改变后也相应发生改变 这种时候就有问题了 所以很有必要了解对象的深拷贝 以及深拷贝的几种方式 new 对象 手动 new 新的对象 一个属性一个属性的
  • 拷贝构造函数与深拷贝和浅拷贝

    拷贝构造函数是一种特殊的构造函数 函数的名称必须和类名称一致 它必须的一个参数是本类型的一个引用变量 作用就是用来复制对象 在使用这个对象的实例来初始化这个对象的一个新的实例 类中可以存在多个拷贝构造函数 拷贝构造函数的调用时机 当函数的参
  • C++:派生类的默认构造函数和拷贝构造函数调用基类构造函数的机制(含程序验证)

    1 如果基类定义了不带参数的默认构造函数 则编译器为派生类自动生成的默认构造函数会调用基类的默认构造函数 2 如果基类定义了拷贝构造函数 则编译器为派生类自动生成的拷贝构造函数同样会调用基类的拷贝构造函数 3 如果基类定义了带参数的构造函数
  • 【深入理解C++】拷贝构造函数

    文章目录 1 拷贝构造函数 2 默认的拷贝操作 3 默认拷贝构造函数 4 何时调用拷贝构造函数 1 拷贝构造函数 拷贝构造函数是构造函数的一种 当利用已存在的对象创建一个新对象时 就会调用新对象的拷贝构造函数进行初始化 拷贝构造函数的格式是
  • 对java中的List进行深拷贝,并进行删除测试

    List
  • 深入C++的拷贝构造和赋值函数 (深拷贝,浅拷贝)

    参考了 点击打开链接以及 高质量程序设计指南C C语言 说明 拷贝构造函数是一种特殊的构造函数 相同类型的类对象是通过拷贝构造函数来完成整个复制过程的 函数的名称必须和类名称一致 它的参数是唯一的 该参数是const类型的引用变量 例如 类

随机推荐

  • CSPP 数据的机器级表示

    寄存器 intel x86 64 调用寄存器与被调用寄存器 因为要保证在调用函数返回后寄存器的值恢复为未被调用之前 所以下面的例子运用pushq指令保存被调用寄存器rbx的值 函数 gcc产生的指令指示操作数的大小 寄存器的作用 rax存储
  • 华为OD机试 - 勾股数元组(Java)

    题目描述 如果3个正整数 a b c 满足a 2 b 2 c 2的关系 则称 a b c 为勾股数 著名的勾三股四弦五 为了探索勾股数的规律 我们定义如果勾股数 a b c 之间两两互质 即a与b a与c b与c之间均互质 没有公约数 则其
  • Torch安装

    安装步骤参考官网http torch ch docs getting started html 安装过程中可能遇见的问题 1 执行命令 git clone https github com torch distro git torch re
  • python后端学习(九)GIL、深/浅拷贝、私有化、import、封装继承多态

    GIL面试题 描述Python GIL的概念 以及它对python多线程的影响 编写一个多线程抓取网页的程序 并阐明多线程抓取程序是否可比单线程性能有提升 并解释原因 Guido的声明 http www artima com forums
  • Android微信页面缓存清理,安卓用户如何彻底清理微信大量缓存?4招让你彻底解决内存烦恼...

    原标题 安卓用户如何彻底清理微信大量缓存 4招让你彻底解决内存烦恼 作为一个64G版的安卓用户 现在隔三差五就要对手机的内存进行清理 更不用说还在用16G的你了 如果经常出现手机的提醒你的存储容量几乎已满时 你是不老是跟小编以前一样去相册里
  • (Java) 算法题:2的N次方

    题目描述 原题链接 2的N次方 对于一个整数N 512 lt N lt 1024 计算2的N次方并在屏幕显示十进制结果 输入描述 输入一个整数N 512 lt N lt 1024 输出描述 2的N次方的十进制结果 输入例子1 512 输出例
  • 实现即时通讯的几种方式

    文章目录 1 短轮询 2 长轮询 3 SSE 4 WebSocket 总结 在 Web 应用程序中 实现即时通讯是一件常见的任务 为了实现即时通讯 我们需要使用一些特殊的技术和协议来建立一个实时连接 以便实时更新数据 在本文中 我们将介绍几
  • 本地编辑shopify主题的第一种方式

    先进入Shopify商店后台 新建应用程序 填写完无关紧要的信息后 把Theme templates and theme assets权限设置为读写访问权限并保存 然后复制密码 这表示可以通过这个密码对主题进行读写修改了 然后按照命令获取主
  • k8s-(五)最全的安装教程(使用kubeadm在Centos7上部署kubernetes1.18)以及安装异常问题记录

    k8s使用kubeadm进行安装步骤 使用kubeadm安装k8s会简单很多 一直想总结写一篇简单明了的安装教程 希望能有用 k8s在2020年初发布的第一个版本是1 18 0 目前最新版本是1 19 4 并且1 20的版本应该会在年底发布
  • Oracle PL/SQL中的循环处理(sql for循环)

    今年春节算是休了个长假 调整好心态 迎接新一年的挑战 今天来说下Oracle中的循环迭代处理 因为从自己的博客统计中看到 不少网友都搜索了关键字 SQL FOR循环 所以打算在这里说下个人的理解 PL SQL也和我们常用的编程语言一样 提供
  • 真香!用python做副业,月赚1W+,别被死工资拖累

    被压垮的打工人 你还好吗 房贷车贷 上老下小 日常开销 但你的收入有多少 所以你不敢生病 甚至不敢回家 就为了每个月那么点死工资 还得天天加班 然而忙忙忙 却变成了 穷忙族 成为了职场废人 其实很多人都想改变现状 想学点什么的 但就是不知从
  • c语言 字母消消乐,消消乐(C语言版)

    消消乐 游戏规则很简单 点击的位置颜色相连的区域抵消 实现思路 从点击位置开始深搜 递归 记录搜索的坐标并抵消 贴上关键代码 map数组保存每个点的颜色 state保存是否搜索过 判断当前点是否满足条件 并且未搜索过 int isValid
  • VS Code 快捷键(中英文对照版)

    标签 空格分隔 visual studio code 常用 General 按 Press 功能 Function Ctrl Shift P F1 显示命令面板 Show Command Palette Ctrl P 快速打开 Quick
  • 快速解决QQ自动下载腾讯视频播放器

    使用电脑QQ播放视频时 QQ总是会使用默认安装的腾讯视频播放器打开 可是他的这个播放器非常的卡 自己设置的默认不使用播放仍然不起作用 用geek观察了一下电脑 确实没发现腾讯视频 于是在播放视频的时候打开任务管理器 终于发现了腾讯视频播放器
  • 原理图以及vhdl设计一位全加器

    原理图设计以及VHDL设计 一位加法器 全加器原理 全加器真值 输出表达式 原理图设计法 VHDL设计法 代码如下 全加器是用门电路实现两个二进制数相加并求出和的组合线路 称为一位全加器 一位全加器可以处理低位进位 并输出本位加法进位 多个
  • 【目标检测】各种方法中比较难理解的地方

    1 评价指标mAP 全网最清楚的解释 强推 原文链接 http blog sina com cn s blog 9db078090102whzw html 理解的关键点在于每一次的precision和recall计算都是在top X的基础上
  • VMware虚拟机装win7教程

    VMware虚拟机装win7教程 前言 一 VMware虚拟机装win7 二 装Vmware Tools 1 初步装好win7后 要装Vmware Tools 2 搞不定的接着往下看 也是本人遇到的问题 总结 前言 昨晚想要在win10系统
  • 初识RecyclerView

    使用之前 implementation com android support recyclerview v7 26 1 0 添加v7的依赖 不然Recyclerview不给用 1 Xml布局 此处布局文件有两个 一个是整体的父布局文件 代
  • 利用Python实现几种常见排序算法

    一 排序算法概览 插入排序 直接插入排序 二分法插入排序 选择排序 直接选择排序 堆排序 交换排序 冒泡排序 快速排序 归并排序 二 代码实现 1 直接插入排序 最简单直接的一种方式 序列在排序中可分为左边已排序部分和右边未排序部分 每次从
  • 拷贝构造函数与深拷贝和浅拷贝

    拷贝构造函数是一种特殊的构造函数 函数的名称必须和类名称一致 它必须的一个参数是本类型的一个引用变量 作用就是用来复制对象 在使用这个对象的实例来初始化这个对象的一个新的实例 类中可以存在多个拷贝构造函数 拷贝构造函数的调用时机 当函数的参