C++ 赋值、浅拷贝、深拷贝和零拷贝解析

1. 浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
2. 深拷贝
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的,示意图大致如下:
C++ 赋值、浅拷贝、深拷贝和零拷贝解析_第1张图片
3. 赋值与浅拷贝差异
赋值:
把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
浅拷贝:
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
对比赋值与浅拷贝会对原对象带来哪些改变:
对象赋值操作:

// 对象赋值
var obj1 = {
   'name' : 'zhangsan',
   'age' :  '18',
   'language' : [1,[2,3],[4,5]],
};

var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)

对象赋值后结果如下:
C++ 赋值、浅拷贝、深拷贝和零拷贝解析_第2张图片

浅拷贝操作:
// 浅拷贝
var obj1 = {
   'name' : 'zhangsan',
   'age' :  '18',
   'language' : [1,[2,3],[4,5]],
};

var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
   var dst = {};
   for (var prop in src) {
       if (src.hasOwnProperty(prop)) {
           dst[prop] = src[prop];
       }
   }
   return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)

浅拷贝操作结果:
C++ 赋值、浅拷贝、深拷贝和零拷贝解析_第3张图片
上面例子中,obj1是原始数据,obj2是赋值操作得到,而obj3浅拷贝得到。我们可以很清晰看到对原始数据的影响,具体请看下表:
C++ 赋值、浅拷贝、深拷贝和零拷贝解析_第4张图片
4. Linux中的零拷贝
在 Linux 中,减少拷贝次数的一种方法是调用 mmap() 来代替调用 read.
首先,应用程序调用了 mmap() 之后,数据会先通过 DMA 被复制到操作系统内核的缓冲区中去。接着,应用程序跟操作系统共享这个缓冲区,这样,操作系统内核和应用程序存储空间就不需要再进行任何的数据复制操作。应用程序调用了 write() 之后,操作系统内核将数据从原来的内核缓冲区中复制到与 socket 相关的内核缓冲区中。接下来,数据从内核 socket 缓冲区复制到协议引擎中去,这是第三次数据拷贝操作。
通过使用 mmap() 来代替 read(), 已经可以减半操作系统需要进行数据拷贝的次数。当大量数据需要传输的时候,这样做就会有一个比较好的效率。但是,这种改进也是需要代价的,使用 mma()p 其实是存在潜在的问题的。当对文件进行了内存映射,然后调用 write() 系统调用,如果此时其他的进程截断了这个文件,那么 write() 系统调用将会被总线错误信号 SIGBUS 中断,因为此时正在执行的是一个错误的存储访问。

你可能感兴趣的:(编程基础,c++,linux,c语言,内核)