👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨
目录
- 一、问题引入
- 二、引用计数
- 三、写时拷贝
- 四、vs下的sizeof(对象)
一、问题引入
我们知道,默认不写拷贝构造函数和赋值运算符重载的话,编译器会自动生成,当然也可以手动编写。如果不手动编写,编译器会默认生成一个浅拷贝,这种拷贝方式对于内置类型或者是自定义类型的成员变量来说是没有问题的
但是如果类中有动态分配内存的指针变量,我们就会发现浅拷贝的时候会出现以下两个问题:
- 共用同一块空间会被析构两次
- 修改一个对象就会影响另外一个
而我们知道,深拷贝的特性就是会额外开辟一块空间,那有没有不额外开销内存来达到类似深拷贝的功能呢?答案当然是有的!
二、引用计数
引用计数是一种基于对象引用数量的内存管理方式。每个对象都会维护一个引用计数器,记录当前有多少个指针指向该对象。当一个新的指针指向该对象时,引用计数加一;当一个指针不再指向该对象时,引用计数减一。当引用计数为零时,表示没有任何指针指向该对象,系统可以安全地释放该对象所占用的内存。因此,引用计数可以解决浅拷贝析构两次的问题
三、写时拷贝
在写时接贝的机制下,当一个对象被复制时,实际上并没有立即进行内存的复制。而是在需要修改其中一个对象时,才会进行实际的内存拷贝操作将原始对象和副本对象分离开来。
具体的实现方式可以理解为:在对象被复制时,原始对象和副本对象共享同一块内存。当要对其中一个对象进行修改时,系统会先检查该对象的引用计数。如果引用计数为1(即只有当前对象指向该内存块),则可以直接修改该内存块;如果引用计数大于1(即有其他对象也指向该内存块),则会进行实际的内存复制操作,将原始对象和副本对象分离开来,用人话来说就会进行深拷贝,然后再进行修改。
有的人就会想,写时拷贝还是要进行深接贝,好像没什么用。这其实是人博弈的心理,例如:有的人就用深拷贝,但是就是不对其内容进行修改,那么这样的深接贝代价太大了。而写时拷贝只有在需要修改时才会进行实际的内存复制操作,避免了不必要的复制。节省内存开销。
四、vs下的sizeof(对象)
以下代码输出的结果是什么?
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1("hello world");
cout << sizeof(s1) << endl;
return 0;
}
【个人分析】
我们知道,string
的底层结构是:
class string
{
char* str;
size_t size;
size_t capacity;
};
根据结构体的内存对齐规则:
从上分析出结果是12
【vs下的输出结果】
为什么输出的结果是28
?
实际上,vs
这个编辑器对string
对象做了一个以空间换时间的处理。vs
给每个对象加了一个长度为16
的buff
数组。当对象的长度不足16
的时候,不需要开空间,直接放到这个数组中,当长度大于等于16的时候才进行开空间。而hello world
的长度是小于16
的。所以最后的结果为:12 + 18 = 28