调用拷贝构造函数的几种情况(附面试题)

2023-11-19

1. 深拷贝和浅拷贝(拷贝构造函数的使用)

 

有时候需要自己定义拷贝构造函数,以避免浅拷贝问题。

在什么情况下需要用户自己定义拷贝构造函数:

一般情况下,当类中成员有指针变量、类中有动态内存分配时常常需要用户自己定义拷贝构造函数。

 

在什么情况下系统会调用拷贝构造函数:(三种情况)

(1)用类的一个对象去初始化另一个对象时

(2)当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用

(3)当函数的返回值是类的对象或引用时

 

简单示例:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class A  
  5. {  
  6. private:  
  7.     int a;  
  8. public:  
  9.     A(int i){a=i;}  //内联的构造函数  
  10.     A(A &aa);  
  11.     int geta(){return a;}  
  12. };  
  13.   
  14. A::A(A &aa)     //拷贝构造函数  
  15. {  
  16.     a=aa.a;  
  17.     cout<<"拷贝构造函数执行!"<<endl;  
  18. }  
  19.   
  20. int get_a(A aa)     //参数是对象,是值传递,会调用拷贝构造函数  
  21. {  
  22.     return aa.geta();  
  23. }  
  24.   
  25. int get_a_1(A &aa)  //如果参数是引用类型,本身就是引用传递,所以不会调用拷贝构造函数  
  26. {  
  27.     return aa.geta();  
  28. }  
  29.   
  30. A get_A()       //返回值是对象类型,会调用拷贝构造函数。会调用拷贝构造函数,因为函数体内生成的对象aa是临时的,离开这个函数就消失了。所有会调用拷贝构造函数复制一份。  
  31. {  
  32.     A aa(1);  
  33.     return aa;  
  34. }  
  35.   
  36. A& get_A_1()    //会调用拷贝构造函数,因为函数体内生成的对象aa是临时的,离开这个函数就消失了。所有会调用拷贝构造函数复制一份。  
  37. {  
  38.     A aa(1);  
  39.     return aa;  
  40. }  
  41.   
  42. int _tmain(int argc, _TCHAR* argv[])  
  43. {  
  44.     A a1(1);  
  45.     A b1(a1);           //用a1初始化b1,调用拷贝构造函数  
  46.     A c1=a1;            //用a1初始化c1,调用拷贝构造函数  
  47.   
  48.     int i=get_a(a1);        //函数形参是类的对象,调用拷贝构造函数  
  49.     int j=get_a_1(a1);      //函数形参类型是引用,不调用拷贝构造函数  
  50.   
  51.     A d1=get_A();       //调用拷贝构造函数  
  52.     A e1=get_A_1();     //调用拷贝构造函数  
  53.   
  54.     return 0;  
  55. }  


 

附:一个面试试题

修改下面程序中的错误:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class NameStr  
  5. {  
  6. private:  
  7.     char *m_pName;  
  8.     char *m_pData;  
  9. public:  
  10.     NameStr()  
  11.     {  
  12.         static const char s_szDefaultName[]="Default name";  
  13.         static const char s_szDefaultStr[]="Default string";  
  14.         strcpy(m_pName,s_szDefaultName);  
  15.         strcpy(m_pData,s_szDefaultStr);  
  16.     }  
  17.     ~NamedStr(){}  
  18.     NameStr(const char* pName,const char* pData)  
  19.     {  
  20.         m_pData=new char[strlen(pData)];  
  21.         m_pName=new char[strlen(pData)];  
  22.     }  
  23.   
  24.     void Print()  
  25.     {  
  26.         cout<<"Name:"<<m_pName<<endl;  
  27.         cout<<"String:"<<m_pData<<endl;  
  28.     }  
  29. };  
  30.   
  31. int _tmain(int argc, _TCHAR* argv[])  
  32. {  
  33.     NameStr* pDefNss=NULL;  
  34.   
  35.     pDefNss=new NameStr[10];  
  36.     NameStr ns("hello","world");  
  37.   
  38.     delete pDefNss;  
  39.   
  40.     return 0;  
  41. }  


分析:

1. 第14、15行,strcpy(m_pName,s_szDefaultName) 对未分配内存空间的字符指针赋值会出现异常。

2. 第20行、21行,m_pData=new char[strlen(pData)] 应该为m_pData=new char[strlen(pData)+1] ,并且应该为最后一个字符赋值为'\0'。

3. 析构函数中,应该处理字符指针内存空间的释放。

4. 因为类的成员变量中有指针变量,因此应该编写类的拷贝构造函数和赋值函数,防止浅拷贝。

5. pDefNss是一个对象数组,delete时应该是delete [ ]pDefNss。

 

比较规范的代码如下:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. //NameStr类的声明  
  5. class NameStr  
  6. {  
  7. private:  
  8.     char *m_pName;  
  9.     char *m_pData;  
  10. public:  
  11.     NameStr();      //默认拷贝构造函数  
  12.   
  13.     ~NameStr(); //析构函数声明  
  14.   
  15.     NameStr(const char* pName,const char* pData);   //带参构造函数的声明  
  16.   
  17.     NameStr(const NameStr& temp);   //拷贝构造函数的声明  
  18.   
  19.     NameStr& operator= (const NameStr& temp);   //重载=运算符  
  20.   
  21.     void Print();   //输出对象内容  
  22. };  
  23.   
  24. //默认构造函数的实现  
  25. NameStr::NameStr()    
  26. {  
  27.     static const char s_szDefaultName[]="Default name";  
  28.     static const char s_szDefaultStr[]="Default string";  
  29.   
  30.     m_pData=new char[strlen(s_szDefaultStr)+1];     //不能为为分配内存空间的字符指针赋值  
  31.     m_pName=new char[strlen(s_szDefaultName)+1];  
  32.   
  33.     strcpy(m_pName,s_szDefaultName);        //更规范的方式是使用strncpy函数进行拷贝  
  34.     m_pName[strlen(s_szDefaultName)]='\0';  
  35.     strcpy(m_pData,s_szDefaultStr);  
  36.     m_pData[strlen(s_szDefaultStr)]='\0';  
  37. }  
  38.   
  39. //析构函数的实现  
  40. NameStr::~NameStr()  
  41. {  
  42.     delete []m_pData;  
  43.     delete []m_pName;  
  44. }  
  45.   
  46. //带参构造函数的实现  
  47. NameStr::NameStr(const char* pName,const char* pData)  
  48. {  
  49.     m_pData=new char[strlen(pData)+1];      //开辟内存空间  
  50.     m_pName=new char[strlen(pName)+1];  
  51.   
  52.     strcpy(m_pData,pData);  
  53.     m_pData[strlen(pData)]='\0';  
  54.     strcpy(m_pName,pName);  
  55.     m_pName[strlen(pName)]='\0';  
  56. }  
  57.   
  58. //拷贝构造函数的实现  
  59. NameStr::NameStr(const NameStr& temp)  
  60. {  
  61.     m_pData=new char[strlen(temp.m_pData)+1];         
  62.     m_pName=new char[strlen(temp.m_pName)+1];  
  63.   
  64.     strcpy(m_pData,temp.m_pData);  
  65.     m_pData[strlen(temp.m_pData)]='\0';  
  66.     strcpy(m_pName,temp.m_pName);  
  67.     m_pName[strlen(temp.m_pName)]='\0';  
  68. }  
  69.   
  70. //重载=运算符的实现  
  71. NameStr& NameStr::operator=(const NameStr& temp)      
  72. {  
  73.     //首先要进行检查,防止自身复制  
  74.     if(&temp==this//this是一个指针,表示本对象的地址。&temp是temp对象的指针。  
  75.     {  
  76.         return *this;  
  77.     }  
  78.   
  79.     //释放原有的内存空间  
  80.     delete []m_pData;  
  81.     delete []m_pName;  
  82.   
  83.     //分配新的内存空间  
  84.     m_pData=new char[strlen(temp.m_pData)+1];         
  85.     m_pName=new char[strlen(temp.m_pName)+1];  
  86.   
  87.     //进行拷贝  
  88.     strcpy(m_pData,temp.m_pData);  
  89.     m_pData[strlen(temp.m_pData)]='\0';  
  90.     strcpy(m_pName,temp.m_pName);  
  91.     m_pName[strlen(temp.m_pName)]='\0';  
  92.   
  93.     //返回本对象的引用  
  94.     return *this;  
  95. }  
  96.   
  97. inline void NameStr::Print()  
  98. {  
  99.     cout<<"Name:"<<m_pName<<endl;  
  100.     cout<<"String:"<<m_pData<<endl;  
  101. }  
  102.   
  103. //程序入口  
  104. int _tmain(int argc, _TCHAR* argv[])  
  105. {  
  106.     NameStr* pDefNss=NULL;  
  107.   
  108.     pDefNss=new NameStr[3];  
  109.     NameStr ns("hello","world");  
  110.   
  111.     delete []pDefNss;  
  112.   
  113.     NameStr ns1=ns;  
  114.   
  115.     return 0;  
  116. }  
转自: http://blog.csdn.net/hyhyl1990/article/details/7957604
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

调用拷贝构造函数的几种情况(附面试题) 的相关文章

  • Qt5实现与单片机ATS89S51通信

    Qt实现与单片机直接的通信上位机 单片机代码 测试环境 项目目标 实现效果 关键通信类 QSerialport 总结 这是我大二下学期的单片机课设做的一个小项目 实现上位机与下位机之间的通信 测试环境 开发环境 Qt5 96 Mingw32
  • fastcgi的环境变量

    FCGI ROLE RESPONDER SCRIPT FILENAME scripts 5 cgi QUERY STRING aaa 11111111111111 bbb 2222222222222222 ccc 3333333333333
  • C++操作SQLite数据库

    准备工作 在使用C 操作SQLite之前 需要获得sqlite3 h sqlite3 lib sqlite3 dll 大家可以在 这里 下载 并将这3个文件导入VC 工程中 其中sqlite3 dll文件放到Debug文件夹里 SQLite
  • IUnknown—COM和MFC

    转自 http hi baidu com zhangqiuxi blog item 6d9603ad9c8fe5084b36d6a0 html 问题 我用MFC编写COM程序有一段时间了 知道如何使用宏和嵌套类 以及如何在嵌套类中处理IUn
  • Qt5学习之路(vs2012下创建一个QT应用程序)2013-10-14

    刚开始学习QT在网上找的资料基本都是使用QT Create进行开发的 VS下开发的学习资料感觉很少很难找的到 视频教程也基本没看到过貌似 因为我们研发中心是使用MFC进行开发开发工具是VS2010 使用QT开发的话基本我们不会再使用QT C
  • C语言pcre库的使用及验证IP地址的合法性

    PCRE是一个用C语言编写的正则表达式函数库 它十分易用 同时功能也很强大 性能超过了POSIX正则表达式库和一些经典的正则表达式库 在使用PCRE库时 首先肯定是需要安装pcre的 不过一般的系统都会有自带的PCRE库 不过如果想使用最新
  • 编写程序模拟完成动态分区存储管理方式的内存分配和回收。

    usr bin python coding utf 8 class Table object 空闲分区表 0 开始地址 1 长度 freeTable 占用分区表 0 程序名 1 开始地址 2 长度 useTable def init sel
  • 简析多级指针解引用

    转自 简析多级指针解引用 指针是C语言中公认的最为强大的语法要素 但同时也是最难理解的语法要素 它曾给程序员带来了无数麻烦和痛苦 以致于在C语言之后诞生的很多新兴 语言中我们再也难觅指针的身影了 下面是一个最简单的C语言指针的例子 int
  • typedef struct 用法详解

    typedef为C语言的关键字 作用是为一种数据类型定义一个新名字 当typedef与结构结合使用时 会有一些比较复杂的情况 而且在C语言和C 里面有略有差别 本文将详细讲解typedef struct的用法 第一篇 typedef str
  • 使用QZXing生成并解析二维码

    QZxing 是对 zxing 的一个封装 用于在 Qt 程序中加入条形码和二维码识别的功能 这里就讲讲如何编译和使用这个库 前几年 QZXing 的代码是放到 sourceforge net 上的 现在迁移到了 github com 所以
  • GDAL多光谱与全色图像融合简单使用

    目录 简述 C 代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 最近在GDAL的代码中看见了gdalpa
  • LeetCode题目笔记——17.19消失的两个数字

    文章目录 题目描述 题目难度 困难 方法一 暴力 代码 代码优化 方法二 数学方法 代码 总结 题目描述 题目直达 题目难度 困难 方法一 暴力 虽然题目说你能在 O N 时间内只用 O 1 的空间找到它们吗 但是也没有限制我们不能用暴力
  • 值得学习与推荐的c/c++框架和函数库

    这几天不上班 翻翻Evernote中记录的一些笔记 刚好有时间把记录的一些好玩链接转载一下 这篇文章里提到的很多库都用过 尤其是图像处理相关库 尤其是opencv及cximage 当时做图像算法时 很多算法就是从上面找来 然后自己修改的 比
  • lua和测试(一)

    lua做为一门高级语言 在游戏产业运用到机会越来越多了 测试掌握几门脚本语言也有一定的重要性 以下对于lua组合输入做出一些引导 测试需要掌握的关于返回数值 主要用到布尔类 前言的指引 lua的语法比较简单和清晰 学过c语言的可以很好的掌握
  • 手把手教你如何写一个三子棋/N子棋的小游戏

    这里写目录标题 第一步 游戏进入界面 第二步 初始化棋盘 第三步 打印棋盘 第四步 玩家和电脑下棋 第五步 判断输赢 三子棋或者N子棋怎么写 让我们先来玩一把 再来看看怎么写 程序运行界面 1为玩游戏 2为清屏 0为退出游戏 我们选1 然后
  • C++:指向类的成员的指针

    引 想必接触过C的朋友们对C语言中指针的概念已经有了深入的了解 如果初步进行了解的朋友可以看一下 C语言基础学习笔记 指针展开来讲的基本知识点包括 指针的概念 指针的定义和初始化及简单使用 指针函数和函数指针 有关指针函数和函数指针的内容上
  • 【C/C++】 - Linux下查找函数头文件 以及 man命令拓展

    背景 比如现在需要找C语言 sleep函数的头文件 使用man来查找 可以先man sleep 可以发现出来的默认是sleep 1 是一个User Commands 明显不是我们需要的 这里提示了 看sleep 3 那我们查看下sleep
  • 检查内存泄露

    自己编写的视频处理程序出现了一个问题 每帧的运行时间随着运行时间在不断增长 很大可能是出现了内存泄露 于是学习了一些查看内存泄露的方法 做了两种尝试 一是VS自带的DEBUG下的检测 view pl html view plain copy
  • 【C++】运算符重载

    加号运算符重载 include
  • C++常见STL容器基本用法

    1 vector include

随机推荐

  • iOS 17 Simulator Failed with HTTP status 400:bad request

    升级 xcode 15 要 ios17 的 sdk 才能运行 但是更新这个 sdk 400 错误了 解决方案 直接去官网下载开发者后台下载dmg文件 使用命令行快速安装即可 https developer apple com documen
  • OCaml简介

    OCaml简介 函数式编程 产生于 优点 ref https zhuanlan zhihu com p 591818090 函数式编程 传统的编程语言 是面向过程 面向对象的 产生于 20世纪80 90年代 产生于法国巴黎高等师范学院 起源
  • 如何在Unity中使用AR Foundation和ARCore创建一个项目并编译到Android 11手机设备中

    最近又开始学如何使用Unity进行AR开发 因为Unity开发的AR Foundation在各种设备 例如Android iOS HoloLens 的原生AR SDK 例如ARCore ARKit Windows 10 SDK 上进行了封装
  • 服务端收发登录注冊流程

    client发包给服务分为主次id struct TCP Command WORD wMainCmdID 主命令码 WORD wSubCmdID 子命令码 一 注冊 1 当在client输入游戏帐号或游戏昵称换行时 进行验证 CS 1 1
  • 《基于spyglass同步设计分析和静态验证》阅读笔记

    常见的CDC问题 亚稳态 data hold数据保持的时间问题 常见的两级触发器同步 多bit信号采用简单的两级触发器同步 CDC中复杂的同步设计 亚稳态总会有概率的存在 单bit信号的CDC同步设计 慢时钟域到快时钟域的同步情况 快时钟域
  • uni-app 运行到MuMu模拟器

    文章目录 1 前言 2 实现流程 2 1 下载MuMu模拟器 2 2 配置全局 adb 2 3 运行到模拟器 2 4 模拟器调为手机版 1 前言 本文使用的模拟器为MuMu模拟器 使用逍遥模拟器会一直卡在 同步手机端程序文件完成 DClou
  • Vue+ElementUI实现从后台动态填充下拉框

    1 首先编写前端代码 将elementUI中的标签写到 vue界面中
  • STM32在休眠模式(Stop/Standby)模式下的关闭看门狗问题的解决

    长期以来一直都认为Stm32启用了IWDG看门狗以后 就不能再使用休眠进入低功耗模式 由于看门狗启动后就不能停止 给很多人带来了困扰 还有很多人放弃了使用看门狗 从而给产品带来一定的不确定性 其实有一个简单的方法可以实现在有看门狗的情况下可
  • Python3:我只用1行代码就下载全网视频,我被我的才华和颜值征服了!!

    you get库使用 1 引言 2 代码实战 2 1 you get介绍 2 2 you get安装 2 3 you get下载视频 2 3 1 指定存储和重命名 2 3 2 查看视频信息 2 3 3 选择需要下载清晰度和格式 2 4 yo
  • 关于华硕飞行堡垒安装Ubuntu时的卡死和 grub引导问题的解决办法

    今天给同学装双系统时发现网上好多博客都不能用 然后结合前辈的一些优点和自己使用deepin和Ubuntu时的感悟 解决了他们两个堡垒关于grub引导和Ubuntu启动时卡死的问题 总结如下 1 关于U盘安装时进不去引导时的解决办法 进入gr
  • 《0基础学安卓逆向》第1集:什么是安卓逆向及环境搭建

    1 安卓逆向是什么 安卓逆向是什么 目前百度知道都没有收录安卓逆向这个词条 大部分能搜索到的资料都是技术人员写的偏技术类的文章 往往充斥着代码和各类工具集合 非技术人员一看就比较懵逼 简单地来说 安卓逆向是对已经打包好的APP进行反编译 源
  • psycopg2中copy_to /copy_from/copy_expert的使用方法

    psycopg2是一个用于在Python中连接和操作PostgreSQL数据库的库 它提供了三个方法copy from copy to和copy expert用于在PostgreSQL数据库中执行COPY操作 copy from方法用于从文
  • Matrix calculus(矩阵微积分)(前四节)

    原文地址 https en wikipedia org wiki Matrix calculus 注 不要把它和几何运算或者是向量运算混淆 前言 在数学中 矩阵微积分是进行多变量微积分的一种特殊符号 特别是在矩阵的空间上 它将关于许多变量的
  • C语言初识

    include
  • 2012.11.23

    刚体变换基本上告一段落 现在开始封装这一部分 以便于重用
  • 成功解决 vscode远程调试python

    welcome to my blog 微软新出的插件 非常方便远程调试 不需要改动代码 简单9步 配置远程调试环境 第一步 按ctrl shift x 输入remote development 安装 第二步 按ctrl shift p 输入
  • MFC进程退出

    MFC软件关闭 进程退出 BOOL CMainFrame DestroyWindow TODO 在此添加专用代码和 或调用基类 m bRunThreadStart 0 while m bRunThreadState m bReadyStat
  • JavaRMI入门详细

    Java RMI入门 定义 RMI 远程方法调用 Remote Method Invocation 它支持存储在不同地址空间的程序级对象之间彼此进行通信 实现远程对象之间的无缝远程调用 Java RMI 1 用于不同虚拟机之间的通信 2 这
  • 毕设分享 自动化专业毕设题目选题推荐 - 单片机 嵌入式 stm32

    文章目录 1前言 2 如何选题 3 选题方向 2 1 物联网方向 2 2 嵌入式开发方向 2 3 人工智能方向 2 4 算法研究方向 2 4 学长作品展示 1前言 近期不少学弟学妹询问学长关于自动化专业相关的毕设选题 学长特意写下这篇文章以
  • 调用拷贝构造函数的几种情况(附面试题)

    1 深拷贝和浅拷贝 拷贝构造函数的使用 有时候需要自己定义拷贝构造函数 以避免浅拷贝问题 在什么情况下需要用户自己定义拷贝构造函数 一般情况下 当类中成员有指针变量 类中有动态内存分配时常常需要用户自己定义拷贝构造函数 在什么情况下系统会调