1.用memcpy函数拷贝vector
2.lodash源码分析——deepclone
3.[UVM源代码研究] 谈谈uvm中的对象对象浅拷贝(shallow copy)与深拷贝(deep copy)
4.还在用BeanUtils拷贝对象?MapStruct才是王者!附源码
用memcpy函数拷贝vector
定义了两个vector,vector里面存的拷贝拷贝是类,是源码源码用否可以直接使用memcpy去复制vector?这个问题涉及C库函数strcpy、memcpy和memmove。对象对象让我们先了解这三种函数,拷贝拷贝然后再解答这个问题。源码源码用盛夏云监控源码
1. strcpy,对象对象 memcpy以及memmove
strcpy, memcpy, memmove都是C库函数,它们的拷贝拷贝原型如下:
strcpy 不需要传入复制的字节数,而memcpy和memmove需要,源码源码用这是对象对象因为memcpy和memmove需要明确知道要复制的字节数。
2. 用memcpy函数拷贝vector
分步骤来考虑这个问题。拷贝拷贝
2.1 初探
我们先考虑vector存放内置类型的源码源码用情况。不同于数组,对象对象vector对象在使用时不会转换成指针,拷贝拷贝因此,源码源码用需要使用取地址符&来得到vector对象的地址。
执行上述代码后,我们发现test1中元素的地址可以正常输出。
然而,当程序退出时,引发"读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突"的异常,这是为什么呢?我们可以发现,test1中元素的地址和test中的是相同的!那么,我们可以做出如下猜测:
当程序退出时,后声明的test1先被释放。轮到test调用其析构函数释放内存时,其中的爬取网络源码元素在test1释放时就已经被释放,再尝试访问这些元素进行内存释放会引发“访问冲突”的异常。
那应该怎么做呢?
我们先给test1分配空间,并默认初始化:
然后,将复制语句改成
此时程序正常运行并正常退出。我们用&test[0], &test1[0]真正地取到了test, test1的首元素的地址,函数按照顺序将test中的元素复制到test1中。
注意:不能写成 的形式,因为sizeof(test)得到的是vector的大小,这是一个固定值,和其中存储的值的数量、类型(除了bool)无关。和我们实际想要复制的长度不同,会引发各种各样的错误。
2.2 进一步探索
考虑vector存放类类型的情况。定义一个MyClass类,执行下列语句,确实先析构了test1中的元素,等到test调用其析构函数时,访问冲突异常。按照第一章中的方式进行修改,变量正常析构,程序正常退出。
注意:
2.3 更进一步
之前提到,STL容器占的字节数和储存的数据的数目和类型(除了bool)无关,是一个固定值。用vector举例,网上很多回答都是易搜文档源码说vector中有三个迭代器变量:start, finish, end_of_storage。这三个迭代器的类型是type* 类型指针。因此vector的大小是固定的,在位机上是(debug版本),在位机上是(debug版本)。
并且有说vector::begin()函数返回start;vector::end()函数返回finish... 不知道是不是版本或者编译器的问题,至少在我在vs中,sizeof(vector)的结果有多种,尝试了一些常见的类型:
观察上述运行结果,除了bool很特殊之外,其他的似乎和上述说法吻合。然而,当我执行下列语句时:
得到的结果为:
只有当type不为bool且在release版本下才满足上述说法。
2.3.1 bool类型的特殊之处
bool类型的变量占1个字节,但是bool类型只有两个取值false和true. 因此,为了提高效率,在vector中,bool变量是按位储存的。为了实现这一优化,vector除了有一般vector的成员变量外,还有两个额外的成员变量:
在位机上,这两个变量都占8个字节,因此在debug版本下,vector比vector多占个字节。release版本下可能只有其中一个变量,具体是哪个我也还不清楚。
一般的vector对象在使用[]运算符时, 返回一个类型为OtherType&的左值;而vector对象访问一个代理引用(proxy reference)而非真正的引用(true reference),并返回一个类型为_Vb_reference<_Wrap_alloc > >的黑客写病毒源码右值,因此不能用该返回值初始化一个bool&:
虽然vb[0]不是一个左值,但是仍然可以通过修改它的值来修改vb:
2.3.2 一般的vector
一种说法是:一般的vector只有一个迭代器:_Compressed_pair<_Alty, _Scary_val> _Mypair,它在位机器上占个字节,begin(),end()函数都返回它。因此debuge版本下sizeof(vector::begin())的值为. 这种说法仅供参考。
参考链接:
STL源码剖析-vector
谈vector的特殊性——为什么它不是STL容器
c语言中memcpy是字节大小, 使用memcpy函数时要注意拷贝数据的长度
C++之strcpy、memcpy、memmove比较
lodash源码分析——deepclone
lodash源码分析——deepclone,基于4..版本
本文从源码阅读初心者的角度,一句一句深入分析lodash的deepclone方法,从入口函数开始,逐步解析每一个关键步骤。
入口函数调用cloneDeep.js,通过掩码位判断是否进行深拷贝与复制symbol类型。
在baseClone.js中,通过内部函数调用baseClone进行主要逻辑处理。先判断对象是否为普通对象,然后使用getTag方法获取对象的类型标识。
getTag方法通过baseGetTag进行判断,获取symbol类型时返回symbol.toStringTag属性。现代浏览器支持返回特定类型标签,如内置对象类型或新出现的类型如Map、Promise等。对于自定义类创建的对象,若无特定标签则返回[object Object]。
继续解析baseClone逻辑,启航代理系统源码重点在于针对不同类型的对象进行区分处理,包括数组、普通对象、函数等。函数和空对象返回{ },不进行深拷贝。
在处理复杂类型如数组和对象时,baseClone采用initCloneArray和copyArray函数优化拷贝过程。对于循环引用问题,通过构造栈结构解决,保证了代码的兼容性和易用性。
对于symbol类型,通过Object.getOwnPropertySymbols方法获取symbol的副本,确保深拷贝操作的完整性。
总结,lodash的deepclone方法通过Object.prototype.toString.call得到对象的类型标识,根据标识进行针对性处理,同时解决循环引用问题,兼容现代浏览器的symbol类型。然而,对于function类型仍然采用引用拷贝,未进行深拷贝处理原型链上的属性。
本文由某初学者撰写,旨在分享lodash deepclone源码分析过程,提供一个从入门到深入理解的路径参考。完成日期:年7月日。
[UVM源代码研究] 谈谈uvm中的浅拷贝(shallow copy)与深拷贝(deep copy)
在探讨UVM(Universal Verification Methodology)中的浅拷贝(shallow copy)与深拷贝(deep copy)之前,我们先对相关概念进行简要介绍,以便于理解以下讨论。浅拷贝和深拷贝是对象编程领域中基本概念,不仅限于系统Verilog(SV)和UVM(Universal Verification Methodology)。
浅拷贝:这一概念涉及的是拷贝对象的指针,即浅拷贝只复制指向对象内存空间的指针,使得目标对象与源对象共享同一内存空间。浅拷贝的局限性在于当内存空间被销毁时,所有指向该空间的指针必须重新定义,否则会导致野指针错误。
深拷贝:与此相反,深拷贝确保源对象和拷贝对象完全独立,两者之间互不影响,包括内存空间内容也被复制一份。例如,基本类型如Int、Double,以及结构体(struct)、枚举(Enum)会自动执行深拷贝,而类类型的对象则需区分浅拷贝与深拷贝。
在UVM中,`uvm_object`类提供了`copy`与`clone`函数来实现对象的拷贝。
`copy`函数为非虚拟、无返回值的函数,不能被重写,但`do_copy`函数为虚拟函数,可以通过重写`do_copy`函数实现对`copy`函数的间接重写。调用`copy`函数前,目标对象需先创建,以实现源对象内部对象的深拷贝赋值,而不会对目标对象本身分配空间。
`clone`函数为虚拟函数,返回`uvm_object`类型,可以被重写。由于返回值类型限制,`clone`只能通过`$cast`来实现目标对象类型的转换,而不能直接赋值。`clone`函数返回一个指向源对象类型的`uvm_object`句柄,因此目标对象类型必须与源对象一致(通过`$cast`检查),以确保成功执行`clone`操作,且目标对象不需要事先分配空间,因为`clone`会自动分配新空间。
`copy`函数的实现中,除了`do_copy`之外的第行的`__m_uvm_field_automation(rhs, UVM_COPY, "")`完成了在`field_automation`中的配置实现。如果未重写`do_copy`函数,则所有拷贝行为依赖于`__m_uvm_field_automation`函数。
`uvm_object_defines.svh`文件在第行实现了将`copy`传入参数转换为局部变量`local_data__`,该变量类型为通过`uvm_object_untils_begin`传入的参数类型。`local_data__`在后续的`uvm_field_automation`宏中根据传入的标志位进行相应操作,以`uvm_field_object`为例。
在`uvm_field_object`中,关于`UVM_COPY`的具体操作表明,调用`copy`的源对象不能为空。如果`FLAG&UVM_NOCOPY`位为1,则直接结束代码执行。如果`FLAG&UVM_REFERENCE`位为1,或者`local_data__.ARG == null`,则将目标对象的`ARG`对象句柄指向源对象的`ARG`句柄。这种做法对于未分配空间的对象赋值,以避免错误。`UVM_REFERENCE`的应用场景主要针对`uvm_component`类型的对象注册,确保在进行`copy`和`clone`时执行浅拷贝,避免深拷贝导致的问题。
`uvm_component`类型在`copy`时默认执行深拷贝,而`UVM_REFERENCE`标志位则实现浅拷贝。例如,在`apb_env`中,`bus_monitor`和`bus_collector`被例化为`master`中的`monitor`和`collector`,同时`cfg`对象也传递给`master`。通过`field_automation`的修改,可以观察到`uvm_top`在打印树型结构时,`apb_monitor`和`cfg`对象的打印信息。
总结而言,UVM中的默认拷贝/克隆操作为深拷贝,`UVM_REFERENCE`标志位用于实现浅拷贝。理解这些概念对于在UVM中进行对象拷贝时避免错误至关重要。
还在用BeanUtils拷贝对象?MapStruct才是王者!附源码
MapStruct 是一个强大的 Java 代码生成工具,专用于简化 JavaBean 类型之间的映射实现,尤其在多层应用中实体类与数据传输对象(DTO)之间映射的场景中发挥巨大优势。与传统的手工实现映射相比,MapStruct 通过生成高性能且易于理解的映射代码,显著提高了开发效率,降低了错误率。 MapStruct 的核心特点包括: 自动代码生成:MapStruct 作为编译器插件,在编译时自动为映射接口生成映射代码,实现对象属性的快速映射。 性能优化:生成的映射代码基于普通方法调用,高效且类型安全,支持快速开发和错误检查。 约定优于配置:默认提供了丰富的映射规则,减少配置复杂性,但允许用户自定义实现特殊映射行为。 以下是 MapStruct 的基本使用流程: 引入依赖:确保在项目中正确配置 MapStruct 与 Lombok 的版本兼容性。 定义实体类和 DTO 类:创建需要映射的对象。 创建映射接口:定义映射方法,约定映射规则。 生成映射代码:编译项目,MapStruct 会自动生成实现类,包含所有定义的映射逻辑。 使用映射接口:在客户端代码中注入映射接口,调用映射方法完成对象间的转换。 除了基础用法,MapStruct 还提供了更高级的特性: @Mapper 注解:用于标记映射接口,激活代码生成。 @Mapping 属性:用于配置映射规则,支持多种映射策略,如通过源属性、表达式或常量。 @Mappings、@MappingTarget 等注解:支持更复杂、动态的映射逻辑,如更新已有对象的属性。 扩展功能:如支持多个对象映射至单个对象等高级用法。 MapStruct 与传统拷贝方法的对比显示,它在处理大数据量时具有显著的性能优势。在性能测试中,MapStruct 的表现优于其他常见拷贝工具,如 Apache BeanUtils、cglib 等。在实际应用中,选择 MapStruct 作为对象映射工具,尤其在需要处理大量数据时,能够显著提升系统性能,优化资源利用。