Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1743329
  • 博文数量: 1493
  • 博客积分: 38
  • 博客等级: 民兵
  • 技术积分: 5834
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-19 17:28
文章分类

全部博文(1493)

文章存档

2016年(11)

2015年(38)

2014年(137)

2013年(253)

2012年(1054)

2011年(1)

分类:

2012-12-09 16:33:03

   深拷贝和浅拷贝一定是很多C++菜鸟比较难以理解的一个知识点,很多老鸟估计也不是很明白其内部的原理。其实这涉及到C 内部模型的两个概念,即Default Memberwise Initialization 和bitwise copy semantics。这两个名字是不是感觉很怪,下面我们就来慢慢分析。

   Default Memberwise Initialization: 这是C 模型的内部一种实现方案,其原理就是对于同一类的两个对象直接的赋值进行的暗箱操作。说白了,就是将一个对象的内存空间中的数据,原封不动的拷贝出另一份来填满另一个对象的内存。(关于类对象内存空间的布局概念可以关注我另一篇关于对象布局的介绍)。看了例子:

点击(此处)折叠或打开

  1. class string{
  2. public:
  3. private:
  4.    char *str;
  5.    int len;
  6. };

  7. string noun("book");
  8. string verb = noun;

  9. 赋值操作后其实就是做了如下工作

  10. verb.str = noun.str;
  11. verb.len = noun.len;
如果该类内含其他类的对象作为自己的成员变量的话,那赋值操作并不会对该对象变量进行赋值,而是递归的对其内部数据成员赋值(原理还是有关对象内存布局)。
   从以上的例子,我想大家应该能联想到了吧!  如果类中有个指针成员变量,而其指向堆中的一片区域,然而在赋值过程中,根据memberwise的概念,只是将指针的值进行了赋值,这样一来,这两个对象中的指针变量自然都是指向同一片内存区域了,即所谓的浅拷贝。所以这时就需要程序员自己来实现拷贝构造函数来完成那片堆内存的拷贝赋值操作,即所谓的深拷贝。

================================来一段漂亮的分割线=========================================

   通过上述所说,可以发现,在没有自定义拷贝构造函数的时候,执行赋值操作并没有创建出默认的拷贝构造函数,而是由编译器直接进行了memberwise操作。这就证明了一直以来很多新手会犯的概念错误,认为没有拷贝构造函数的话赋值时编译器会创建出一个来。 但是,默认的拷贝构造函数什么时候会创建出来呢?接着我下看!

   这就要提到所谓的bitwise copy semantics。
   bitwise copy semantics:只有在bitwise copy semantics失效的情况下才会创建默认构造函数。以下4中情况会导致失效。
   1.当类内含一个成员对象,而后者的class申明有一个拷贝构造函数
   2.当class继承自一个base class 而后者存在一个拷贝构造函数时
   3.当类声明了一个活多个虚函数
   4.当类派生自y7ige继承串链,其中有一个或多个虚基类
   1,2两种情况,起原理类似于默认构造函数的创建时机(不清楚的朋友可以关注下我对构造函数的原理介绍),这里就不在赘述了。3,4的情况类似,我主要说下第3中情况。
   大家都知道,一个类如果有虚函数成员方法的话,在创建对象的同时,编译器会创建vptr和一个虚表(这两个概念可以参考我的每天和我总结一下-系列 2---基类的析构为何要虚)。如果没有 bitwise copy semantics的作用的话,很容易想到,编译器会用到memberwise来对两个对象的vptr进行复制,大家都懂得,这样最终会带来问题!不知道的人可以去撞墙了 举个例子

点击(此处)折叠或打开

  1. ZooAnmial
  2. bear : public ZooAnimal

  3. bear joyi;
  4. ZooAnimal franny = joyi;

    所以这时候编译器会创建出拷贝构造函数,而它所做的操作就是让franny的vptr不要被赋值成joyi的vptr而是让它保持原有的vptr的所指向的虚表。

   好了 ,说了这么多!总结下其实就是要让大家有一个概念,拷贝构造函数并不是只用来进行赋值初始化的,有时编译器用它还会做其他的事情的

阅读(230) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~