【带头学C++】----- 九、类和对象 ---- 9.4 拷贝构造函数、赋值

目录

9.4 拷贝构造函数、赋值

9.4.1 定义拷贝构造函数

 9.4.2 拷贝构造和无参构造、有参构造的关系

9.4.3 拷贝构造的几种调用形式

1、旧对象给新对象初始化,调用拷贝构造

2、给对象取别名不会调用拷贝构造

3、普通对象作为函数参数,调用函数时会发生拷贝构造 

 4、函数返回值普通对象

9.4.4 拷贝构造的深拷贝和浅拷贝


9.4 拷贝构造函数、赋值

下图截取了C++primer一书中对该定义形式进行了一个简单说明,也是基于C++11标准以上。

9.4.1 定义拷贝构造函数

        对象在几种情况下会被拷贝,如我们初始化变量以及以值的方式传递或返回一个对象。当我们使用了赋值运算符时会发生对象的赋值操作。

  1. 初始化变量:当我们使用一个对象来初始化另一个变量时,会触发对象的拷贝操作。这意味着在新的变量中创建了一个与原始对象相同的副本。

  2. 以值的方式传递或返回对象:当我们将对象作为参数传递给函数或者从函数中返回对象时,也会触发对象的拷贝操作。这样做可以确保在函数调用时对象的值被复制到新的内存空间中,以避免对原始对象的修改。

  3. 赋值运算符:当我们使用赋值运算符将一个对象的值赋给另一个对象时,会触发对象的赋值操作。赋值操作会将原始对象的值复制到新的对象中,并且它们将引用相同的内存空间。

  4. 函数返回一个局部对象的引用或指针:如果函数返回一个局部创建的对象的引用或指针,则需要确保在函数返回后对象仍然有效。一种常见的方式是在函数内部使用new关键字来创建对象,并返回对象的指针。在这种情况下,调用者需要负责释放内存以避免内存泄漏。

  5. 使用拷贝构造函数或拷贝赋值运算符:当我们显式地使用拷贝构造函数或拷贝赋值运算符来创建或赋值对象时,也会触发对象的拷贝操作。这些特殊的成员函数可以用于自定义对象的复制方式,以及处理动态分配的资源(如堆内存)。

拷贝构造:本质是构造函数
拷贝构造的调用时机:旧对象初始化新对象,才会调用拷贝构造

如果用户不提供拷贝构造,编译器会自动提供一个默认的拷贝构造(完成赋值动作——浅拷贝,就是只是复制内容到新对象里面)

此时下面这个你只要写了拷贝构造函数,就不会提供默认的拷贝构造函数。

代码:

#include <iostream>
#include "string.h"
using namespace std;

class Data{
public:
    int num;
public:
    Data(){
        cout<<"无参构造"<<endl;
    }
    Data(int a){
        num = a;
        cout << "有参构造 num"<<num<<endl;
    }
    //拷贝构造函数  定义形式
    Data(const Data &ob){
        //一旦定义了拷贝构造函数,一定要记得赋值,值传递(如果有)
        num = ob.num;
        cout<<"拷贝构造函数"<<endl;
    }
    ~Data(){
         cout<<"析构函数 num = "<<num<<endl;
    }
};
void test01(){
    Data ob1(10);
    Data ob2 = ob1;
    cout<<"ob2.num"<<ob2.num<<endl;
}

int main()
{
    test01();
    return 0;
}

 9.4.2 拷贝构造和无参构造、有参构造的关系

如果用户定义了 拷贝构造或者有参构造,都会屏蔽无参构造

如果用户定义了无参构造或者有参构造,不会屏蔽拷贝构造

9.4.3 拷贝构造的几种调用形式

1、旧对象给新对象初始化,调用拷贝构造

Data ob1(10);

Data ob2 = ob1;//只要是旧对象给新的对象赋值、初始化,就会调用拷贝构造

2、给对象取别名不会调用拷贝构造

Data ob1(10);

Data &ob2 = ob1;//此时不会调用拷贝构造

3、普通对象作为函数参数,调用函数时会发生拷贝构造 

void func(Data ob)    //Data ob=ob1    实参传递给形参,相当于右边的表达式

{//函数的形参不会立即开辟空间,只有等函数调用的时候传入参数时开辟空间

}
int main(){

         Data ob1(100);//有参构造

         func(ob1);//拷贝构造

}

 4、函数返回值普通对象

上述发生了两次拷贝构造(是要开辟空间的),但是ob2为什么没有发生拷贝构造,是因为ob2此时是作为临时对象匿名对象的名称被使用了,是临时的。

但是在Qtcreator和linux下不会发生拷贝上述4的拷贝构造(注意这是出现在98时代的qt和linux下,最新的支持c++11的都会发生拷贝构造了)

以下是最新的输出:

以上只作为简单了解,无需记忆深刻。增加自己的了解就行,有个印象

9.4.4 拷贝构造的深拷贝和浅拷贝

默认的拷贝构造都是浅拷贝。
如果类中没有指针成员,不用实现拷贝构造和析构函数
如果类中有指针成员,且指向堆区空间(堆区空间是程序员自己申请的内存空间,有借有还),必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作。

        浅拷贝是一种简单的拷贝方式,它只是简单地将一个对象的值复制到另一个对象中。对于基本数据类型的成员变量,浅拷贝会直接复制其值。对于指针类型的成员变量,浅拷贝只会复制指针的地址,而不是创建一个新的对象。这意味着如果原始对象和副本对象共享相同的指针,当一个对象修改指针指向的内存时,另一个对象也会受到影响。

        深拷贝则是创建一个对象的完全独立副本的拷贝方式。不仅会复制基本数据类型的成员变量的值,还会为指针类型的成员变量创建新的内存空间,并将原始对象指针指向的内容复制到新的内存空间中。这样生成的副本对象和原始对象是完全独立的,互不影响。

        在C++中,可以通过自定义拷贝构造函数来实现深拷贝。在拷贝构造函数中,可以根据需要使用新的内存空间进行复制操作。例如,可以使用动态内存分配函数(如new)来分配新的内存空间,然后将原始对象的值复制到新的内存空间中。这样可以确保创建的副本对象是一个独立的拷贝。

代码:

#include <iostream>
#include "string.h"
using namespace std;

class Data3{
public:
    char* name;
public:
    //无参构造
    Data3(){
        name = nullptr;
    }
    //有参构造
    Data3(const char* str){
        name = new char[strlen(str)+1];
        strcpy_s(name,strlen(str)+1,name);
        cout<<"有参构造 name = "<<name<<endl;
    }
    //拷贝构造函数  深拷贝
    Data3(const Data3 &ob){
        //赋值,值传递(如果有)
        //为对象的指针成员申请独立的空间
        name = new char[strlen(ob.name) + 1];
        strcpy_s(name,strlen(ob.name)+1,ob.name);
        cout<<"拷贝构造函数"<<endl;
    }
    ~Data3(){
         cout<<"析构函数 name = "<<name<<endl;
         if(name != nullptr){
             delete [] name;
             name = nullptr;
         }
    }
};
void test04(){
    Data3 ob1((char *)"he1lo worldn");
    Data3 ob2 = ob1;
}

int main()
{
    test04();
    return 0;
}

理解一下:深拷贝是涉及对象整个完整的复制,地址和参数都要放到一个新的申请的空间里面。
浅拷贝只是使用旧对象的一些值参数,只是复制值,复制对象的值。但是不会开辟空间。没用新的内存地址的产生。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java用法】Hutool树结构工具-TreeUtil快速构建树形结构的两种方式 + 数据排序

Hutool树结构工具-TreeUtil快速构建树形结构的两种方式 数据排序 一、业务场景二、Hutool官网树结构工具2.1 介绍2.2 使用2.2.1 定义结构2.2.2 构建Tree2.2.3 自定义字段名 2.3 说明 三、具体的使用场景3.1 实现的效果3.2 业务代码3.3 实现自定义字段的排序 四、踩过的坑4.1 坑…

中伟视界:皮带跑偏、异物检测AI算法除了矿山行业应用,还能在钢铁、火电、港口等行业中使用吗?

随着工业化的发展&#xff0c;皮带输送机已经成为各行业中不可或缺的重要设备&#xff0c;但是在使用过程中&#xff0c;由于各种原因&#xff0c;皮带常常出现跑偏问题&#xff0c;给生产运营带来了诸多困扰。不仅仅是矿山行业&#xff0c;钢铁、火电、港口等行业也都面临着皮…

英文论文查重复率网址

大家好&#xff0c;今天来聊聊英文论文查重复率网址&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 英文论文查重复率网址 在撰写英文论文时&#xff0c;查重是确保论文原创性和质量的重要环节快码论文…

LeetCode Hot100 131.分割回文串

题目&#xff1a; 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 方法&#xff1a;灵神-子集型回溯 假设每对相邻字符之间有个逗号&#xff0c;那么就看…

windows11 windows 11 (win11 win 11) 怎么安装 Python3 ? numpy? sounddevice? 声音信号处理库?

首先确认要安装的 sounddevice 库&#xff0c;链接&#xff1a;https://python-sounddevice.readthedocs.io/en/0.4.6/ 根据文档&#xff0c;可知最新的 sounddevice 版本是 0.4.6 进入安装页面查看&#xff0c;发现 Newest sounddevice 可以使用 pip 安装&#xff0c;如下图…

AGM离线下载器使用说明

AGM专用离线下载器示意图&#xff1a; 供电方式&#xff1a; 通过 USB 接口给下载器供电&#xff0c;跳线 JP 断开。如果客户 PCB 的 JTAG 口不能提供 3.3V 电源&#xff0c;或仅需烧写下载器&#xff0c;尚未连接用户 PCB 时&#xff0c;采用此种方式供电。 或者&#xff1a…

私域运营:12个朋友圈经营模板

做私域运营的各位&#xff0c;想必大家都会烦恼朋友圈要发什么才能保证最高效吧&#xff01; 首先&#xff0c;我们需要明确&#xff0c;朋友圈是什么&#xff1f; 朋友圈是我们打造信任感的地方&#xff0c;也是我们的信息能够及时触达用户的重要渠道。很多人都有一个习惯&a…

线性代数基础【1】行列式

第一节 行列式的基本概念和性质 一、基本概念 ①逆序 1,2和2,1是一对逆序 ②逆序数 1,2,3,5,4的逆序数为1;1,3,2,5,4逆序数为4; ③行列式 ④余子数和代数余子数 行列式挖掉一个数(例如aij),将原行列式去掉i行j列的行列式M,则M为余子数,代数余子数记为Aij,如果(ij)为偶数…

leaflet:经纬度坐标转为地址,点击鼠标显示地址信息(137)

第137个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中将经纬度坐标转化为地址,点击鼠标显示某地的地址信息 。主要利用mapbox的api将坐标转化为地址,然后在固定的位置显示出来。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示…

既然UDP更快,为啥这么多年一直用TCP ?

你们好啊&#xff0c;我是老杨。 有点基本技术常识的粉丝朋友都知道&#xff0c;UDP肯定是比TCP快的。 很多人对TCP和UDP的了解很浅&#xff0c;直到自己真的经历了一些通信项目之后&#xff0c;你才会愿意根据实际情况埋头苦学&#xff0c;企图“速成”一下。 要是问你为什…

复杂gRPC之go调用go

1. 复杂的gRPC调用 我们使用了一个较为复杂的proto文件&#xff0c;这个文件的功能主要是用来定位的&#xff0c;详细内容可以看代码中的注解 syntax "proto3"; //指定生成的所属的package&#xff0c;方便调用 option go_package "./"; package route…

KaiwuDB 通过中国信通院“可信数据库”性能与稳定性评测

11月29日&#xff0c;中国信通院 2023 年下半年“可信数据库”评估评测结果正式发布&#xff0c;由 KaiwuDB研发的开务数据库系统 KaiwuDB V2.0 达到信通院时序数据库性能、稳定性测试标准。 至此&#xff0c;KaiwuDB已完成时序数据库基础能力、性能、稳定性全项评测&#xff…

Python Tacacs故障诊断记录

背景&#xff1a;客户现场说我们的设备在3A鉴权时失败&#xff0c;没有认证成功 第一步&#xff0c;先看下我们log 没有明显的错误记录&#xff0c;貌似认证成功了但是确提示认证失败&#xff0c;有点迷 第二步&#xff0c;家里搭建和现场一致的环境&#xff0c;模拟登录发现是…

《文存阅刊》期刊发表简介

《文存阅刊》以“深研文化创新&#xff0c;崇尚科学真理&#xff0c;坚持双百方针&#xff0c;打造学术精品”为办刊宗旨&#xff0c;涵盖艺术、文学、社科等多项内容&#xff0c;适应了文化市场需求&#xff0c;很好的回应了广大文化理论工作者的关切&#xff0c;为下一步打造…

cuda lib 线程安全的要义

1, 概述 cuda lib 线程安全的几个多线程的情景&#xff1a; 单卡多线程&#xff1b; 多卡多线程-每卡单线程&#xff1b; 多卡多线程-每卡多线程&#xff1b; 需要考虑的问题&#xff1a; 每个 cublasHandle_t 只能有一个stream么&#xff1f; 每个cusolverHandle_t 只能有一…

DTS认证

一、什么叫DTS DTS 是“Digital Theatre System“的缩写&#xff0c;是”数字化影院系统“的意思。是一种音频格式&#xff0c;从技术上讲&#xff0c;把音效数据存储到另外的CD-ROM中&#xff0c;使其与影像数据同步。这样不但空间得到增加&#xff0c;而且数据流量也可以相对…

如何运用gpt改写出高质量的文章 (1)

大家好&#xff0c;今天来聊聊如何运用gpt改写出高质量的文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何运用GPT改写出高质量的文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然…

HHDESK右键管理简介

在HHDESK管理文件&#xff0c;除了基本的打开、删除、复制、粘贴、重命名外&#xff0c;还有多种便捷编辑方式。 可以分别以下列模式打开文档&#xff1a; 文本模式即是以文本编辑器打开文档。 1 二进制模式 可进行二进制编辑。 2 JSON模式 可对JSON文件进行直观的解析…

b样条原理与测试

为了保留贝塞尔曲线的优点&#xff0c;同时克服贝塞尔曲线的缺点&#xff0c;b样条在贝塞尔曲线上发展而来&#xff0c;首先来看贝塞尔曲线的定义&#xff1a; 对于贝塞尔中的基函数而言&#xff0c;是确定的&#xff0c;全局唯一的&#xff0c;这导致了如果控制点发生变换将会…

软件测试--selenium安装使用

安装selenium不少人使用pip命令来安装selenium&#xff0c;辛辛苦苦安装完之后&#xff0c;还是不能使用。所以我们可以是直接使用编译器&#xff0c;pycharm直接安装selenium扩展包。 file中点击settings 在Settings中点击Project Interpreter,点击加号就可以安装各种需要的扩…
最新文章