赋值运算符和拷贝构造函数的区别与联系

转载:http://blog.csdn.net/hebbely/article/details/65437510

简述:

C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。拷贝构造函数使用已有的对象创建一个新的对象,赋值运算符是将一个对象的值复制给另一个已存在的对象。区分是调用拷贝构造函数还是赋值运算符,主要是否有新的对象产生。


1、构造函数



① 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存(构造函数的命名必须和类名完全相同

② 首先说一下一个C++的空类,编译器会加入哪些默认的成员函数

·默认构造函数和拷贝构造函数

·析构函数

·赋值函数(赋值运算符)

·取值函数

**即使程序没定义任何成员,编译器也会插入以上的函数! 

注意:构造函数可以被重载,可以多个,可以带参数;析构函数只有一个,不能被重载,不带参数

 

③ 而默认构造函数没有参数,它什么也不做。当没有重载无参构造函数时,

  Person man就是通过默认构造函数来创建一个对象

④下面代码为构造函数重载的实现


[cpp] view plain copy
  1. class Person    
  2. {      
  3. public:  
  4.     Person()     
  5.     {    
  6.         qDebug()<<”无参构造函数”<<endl;    
  7.     }    
  8.     Person(int i):m_i(i) {}  //初始化列表    
  9. private:  
  10.     int m_i;  
  11. }  


2、初步认识拷贝构造函数和赋值运算符


① 拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。

② 在默认情况下(用户没有定义,但是也没有显式的删除),编译器会自动的隐式生成一个拷贝构造函数和赋值运算符。但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符,这样的对象就不能通过值传递,也不能进行赋值运算。


[cpp] view plain copy
  1. class Person  
  2. {  
  3. public:  
  4.   
  5.     Person(const Person& p) = delete//不生成拷贝构造函数  
  6.   
  7.     Person& operator=(const Person& p) = delete//不生成赋值运算符  
  8.   
  9. private:  
  10.     int age;  
  11.     string name;  
  12. };  

上面的定义的类Person显式的删除了拷贝构造函数和赋值运算符,在需要调用拷贝构造函数或者赋值运算符的地方,会提示_无法调用该函数,它是已删除的函数_。

③ 还有一点需要注意的是,拷贝构造函数必须以引用的方式传递参数。这是因为,在值传递的方式传递给一个函数的时候,会调用拷贝构造函数生成函数的实参。如果拷贝构造函数的参数仍然是以值的方式,就会无限循环的调用下去,直到函数的栈溢出


3、调用场合


① 拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象;但是其结果却有些不同,拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。这种区别从两者的名字也可以很轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给另一个对象(已经存在的)。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。

② 调用拷贝构造函数主要有以下场景:

    • 对象作为函数的参数,以传递的方式传给函数。 
    • 对象作为函数的返回值,以的方式从函数返回
    • 使用一个对象给另一个对象初始化

③ 什么时候编译器会生成默认的拷贝构造函数:

A. 如果用户没有自定义拷贝构造函数,并且在代码中使用到了拷贝构造函数,编译器就会生成默认的拷贝构造函数。但如果用户定义了拷贝构造函数,编译器就不在生成。

B. 如果用户定义了一个构造函数,但不是拷贝构造函数,而此时代码中又用到了拷贝构造函数,那编译器也会生成默认的拷贝构造函数。


④ 样例代码如下:


[cpp] view plain copy
  1. class Person  
  2. {  
  3. public:  
  4.     Person(){}  
  5.     Person(const Person& p)  
  6.     {  
  7.         cout << "Copy Constructor" << endl;  
  8.     }  
  9.   
  10.     Person& operator=(const Person& p)  
  11.     {  
  12.         cout << "Assign" << endl;  
  13.         return *this;  
  14.     }  
  15.   
  16. private:  
  17.     int age;  
  18.     string name;  
  19. };  
  20.   
  21. void f(Person p)  
  22. {  
  23.     return;  
  24. }  
  25.   
  26. Person f1()  
  27. {  
  28.     Person p;  
  29.     return p;  
  30. }  
  31.   
  32. int main()  
  33. {  
  34.     Person p;  
  35.     Person p1 = p;    // A  
  36.     Person p2;  
  37.     p2 = p;           // B  
  38.     f(p2);            // C  
  39.   
  40.     p2 = f1();        // D  
  41.   
  42.     Person p3 = f1(); // E  
  43.   
  44.     getchar();  
  45.     return 0;  
  46. }  

上面代码中定义了一个类Person,显式的定义了拷贝构造函数和赋值运算符。然后定义了两个函数:f(),以值的方式参传入Person对象;f1(),以值的方式返回Person对象。在main中模拟了5中场景,测试调用的是拷贝构造函数还是赋值运算符。执行结果如下:


分析如下:

A. 这是虽然使用了"=",但是实际上使用对象p来创建一个新的对象p1。也就是产生了新的对象,所以调用的是拷贝构造函数

B. 首先声明一个对象p2,然后使用赋值运算符"=",将p的值复制给p2,显然是调用赋值运算符,为一个已经存在的对象赋值 。

C. 以值传递的方式将对象p2传入函数f内,调用拷贝构造函数构建一个函数f可用的实参

D. 这条语句拷贝构造函数和赋值运算符都调用了。函数f1以值的方式返回一个Person对象,在返回时会调用拷贝构造函数创建一个临时对象tmp作为返回值;返回后调用赋值运算符将临时对象tmp赋值给p2.

E. 按照4的解释,应该是首先调用拷贝构造函数创建临时对象;然后再调用拷贝构造函数使用刚才创建的临时对象创建新的对象p3,也就是会调用两次拷贝构造函数。不过,编译器也没有那么傻,应该是直接调用拷贝构造函数使用返回值创建了对象p3。


4、深拷贝、浅拷贝


① 通常,默认生成的拷贝构造函数和赋值运算符,只是简单的进行值的复制。例如:上面的Person类,字段只有intstring两种类型,这在拷贝或者赋值时进行值复制创建的出来的对象和源对象也是没有任何关联对源对象的任何操作都不会影响到拷贝出来的对象。反之,假如Person有一个对象为int *,这时在拷贝时还只是进行值复制,那么创建出来的Person对象的int *的值就和源对象的int *指向的是同一个位置。任何一个对象对该值的修改都会影响到另一个对象,这种情况就是浅拷贝

② 系统提供的默认拷贝构造函数工作方式是内存拷贝,也就是浅拷贝。如果对象中用到了需要手动释放的对象,则会出现问题,这时就要手动重载拷贝构造函数,实现深拷贝。


③ 深拷贝与浅拷贝:

浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)

深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。

④ 深拷贝浅拷贝主要是针对类中的指针动态分配的空间来说的,因为对于指针只是简单的值复制并不能分割开两个对象的关联,任何一个对象对该指针的操作都会影响到另一个对象。这时候就需要提供自定义的深拷贝的拷贝构造函数,消除这种影响。通常的原则是:

    • 含有指针类型的成员或者有动态分配内存的成员都应该提供自定义的拷贝构造函数
    • 在提供拷贝构造函数的同时,还应该考虑实现自定义的赋值运算符

⑤ 对于拷贝构造函数的实现要确保以下几点:

    • 对于值类型的成员进行值复制
    • 对于指针和动态分配的空间,在拷贝中应重新分配分配空间
    • 对于基类,要调用基类合适的拷贝方法,完成基类的拷贝

5、总结



    • 拷贝构造函数和赋值运算符的行为比较相似,却产生不同的结果;拷贝构造函数使用已有的对象创建一个新的对象,赋值运算符是将一个对象的值复制给另一个已存在的对象。区分是调用拷贝构造函数还是赋值运算符,主要是否有新的对象产生
    • 关于深拷贝和浅拷贝。当类有指针成员或有动态分配空间,都应实现自定义的拷贝构造函数。提供了拷贝构造函数,最后也实现赋值运算符。
    • 对象不存在,且没用别的对象来初始化,就是调用了构造函数;

      对象不存在,且用别的对象来初始化,就是拷贝构造函数;

      对象存在,用别的对象来给它赋值,就是赋值函数。


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

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

相关文章

硬件有没有35岁危机?

大家好&#xff0c;我篇文章是我的朋友记得诚写的&#xff0c;分享给大家&#xff01;35岁危机指的是&#xff0c;很多企业停止招聘35岁及以上的员工&#xff0c;甚至会裁掉年纪大的人。我们经常说程序员&#xff0c;说互联网从业者有35岁危机。这个危机一方面是自己的焦虑&…

强大的独立日期选择器(date picker)插件 - Kalendae

日期&#xff1a;2012-4-16 来源&#xff1a;GBin1.com 在线演示 本地下载 今天分享一个独立的日期选择插件Kalendae&#xff0c;Kalendae是 一个强大健壮的独立日期选择器。如果你不想使用重量的jQuery UI类库的话&#xff0c;这个插件肯定是一个不错的备选。Kalendae包含了…

35家名企嵌入式/软件秋招岗位等你来!

大家好&#xff0c;我是写代码的篮球球痴。前两天一个小伙伴在微信让我帮忙推荐嵌入式招聘岗位。我实话实说&#xff0c;是有认识的人让我推荐简历的&#xff0c;但是相对于专业的招聘网站&#xff0c;我手里能拿出来的岗位实在是少之又少&#xff0c;专业的招聘网站会对不同的…

尝试梳理下ARM处理器的发展历史

大家好&#xff0c;这篇文章是我的朋友Michael Yao写的&#xff0c;我觉得非常不错&#xff0c;分享给大家。1. 前言本文尝试简单梳理下ARM处理器的发展历史、架构的演进&#xff0c;包括不同处理器的应用方向&#xff0c;但我们重点还是围绕Cortex-A系列展开&#xff0c;也会介…

linux下的CPU、内存、IO、网络的压力测试

一、对CPU进行简单测试&#xff1a; 1、通过bc命令计算特别函数 例&#xff1a;计算圆周率 echo "scale5000; 4*a(1)" | bc -l -q MATH LIBRARY If bc is invoked with the -l option, a math library is preloaded and the default scale is set to 20.…

给你这张图,你能搜索到来历吗

如果我们想让搜索引擎帮我们找到这张图的来历&#xff0c;可能吗&#xff1f; 这是目前搜索引擎做不到的。如果能做到呢&#xff1f; 转载于:https://www.cnblogs.com/shangge/archive/2008/07/23/1249767.html

晚上读内核代码

最近因为要解决一个bug&#xff0c;需要在内核的f_hid.c里面做一些适配&#xff0c;需要把这部分代码研究透彻。在这几天之前我是根本不知道什么是In端点&#xff0c;Out端点&#xff0c;以及什么是endpoint 0的&#xff0c;而且最近的一段时间&#xff0c;我一直是在写应用方面…

Linux进程调度与性能优化 | 真货

作者简介&#xff1a;张毅峰&#xff0c;某主机厂架构师。一、eBPF安全可观测性的前景展望本次分享将从监控和可观测性、eBPF安全可观测性分析、内核安全可观测性展望三个方面展开。1.监控(Monitoring)vs可观测性(Observability)从上图可以看到&#xff0c;监控只是可观测性的冰…

教师生涯由此开始

招聘会时间&#xff1a;2012-03-28 09:00 招聘会地址&#xff1a;就业办507 为加强教师队伍建设&#xff0c;建设教育强县&#xff0c;经研究&#xff0c;我县决定面向全国“985”、“211”工程高等师范院校应届毕业生选聘普通高中和初中教师38名。现将有关事项公告如下&#x…

二叉树前序、中序、后序遍历非递归写法的透彻解析

前言 在前两篇文章二叉树和二叉搜索树中已经涉及到了二叉树的三种遍历。递归写法&#xff0c;只要理解思想&#xff0c;几行代码。可是非递归写法却很不容易。这里特地总结下&#xff0c;透彻解析它们的非递归写法。其中&#xff0c;中序遍历的非递归写法最简单&#xff0c;后序…

深夜看代码2

昨天的文章晚上读内核代码有人评论说好像说了一些什么&#xff0c;好像又没有说什么&#xff0c;所以我到底是在说什么呢&#xff1f;因为今天已经把内核修改好了&#xff0c;自己也测试了&#xff0c;所以这次好好说下&#xff0c;我到底是说了什么&#xff0c;又做了什么。—…

F#学习之路(2) 深刻理解函数(上)

函数在函数式编程语言中是一等公民&#xff0c;是函数式语言中最重要的基本组成元素&#xff0c;也是其名称的由来。 F# 中的函数之如C#中的类&#xff0c;是组织程序结构的最基本单元。是命令式编程语言中函数或OO编程语言中方法的超集。超集&#xff0c;有多强大&#xff1f…

C++ 线程安全的单例模式

转载&#xff1a;https://www.cnblogs.com/ccdev/archive/2012/12/19/2825355.html 废话不多说&#xff0c;常用的代码积淀下来。 一、懒汉模式&#xff1a;即第一次调用该类实例的时候才产生一个新的该类实例&#xff0c;并在以后仅返回此实例。 需要用锁&#xff0c;来保证其…

写代码多就牛逼?

最近遇到了一些人、一些事&#xff0c;然后就想着拿出来总结总结&#xff0c;并谈谈自己的一些看法&#xff0c;所以就有了这篇文章。首先&#xff0c;我们来看看下面遇到过的两种情景。情景1&#xff1a;在工作中经常会遇到这样一些人&#xff1a;要他们实现一些功能&#xff…

推荐12款非常有用的流行 jQuery 插件

jQuery 是一个非常优秀的 JavaScript 框架&#xff0c;在现在的 Web 开发项目中扮演着重要角色。jQuery 使用简单灵活&#xff0c;同时还有许多成熟的插件可供选择&#xff0c;它可以帮助你在项目中加入一些非常好的效果&#xff0c;让网站有更好的可用性和用户体验。今天这篇文…

Linux以及各大发行版介绍

什么是Linux&#xff1f; 也许很多人会不屑的说&#xff0c;Linux不就是个操作系统么。错&#xff01;Linux不是一个操作系统&#xff0c;严格来讲&#xff0c;Linux只是一个操作系统中的内核。内核是什么&#xff1f;内核建立了计算机软件与硬件之间通讯的平台&#xff0c;内核…

有人LeetCode第一题都做不出来

有一个这样的江湖传闻时间是8点30&#xff0c;我不信这个邪把力扣的第一题写一次——题目给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。你可以假设每种输入只会对应一个…

内存池技术介绍

看到一篇关于内存池技术的介绍文章&#xff0c;受益匪浅&#xff0c;转贴至此。 原贴地址&#xff1a;http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html 6.1 自定义内存池性能优化的原理 如前所述&#xff0c;读者已经了解到"堆"和"栈"的…

Linux 应用开发——完整版思维导图

转自我朋友的公众号「嵌入式Linux系统开发」&#xff0c;总结的内容对大家学习是非常有帮助的。目录

你觉得好的代码可能并不是最优的解决方案

晚上我看到了JeffXie 写了一篇关于内存屏障的文章&#xff0c;后面又看到Linus对一次内存屏障修改的建议&#xff0c;所以就有了这篇文章。https://mp.weixin.qq.com/s/H7Pw8xCKcNu41UGaYB648w在我看来&#xff0c;内存屏障谁为了让计算机做更加正确的事情&#xff0c;不希望计…