【C++】运算符重载、迭代器、写时拷贝、内存池

一、运算符的重载

运算符的重载的目的是让自定义类型满足和内置类型相同的逻辑,运算符重载是由一个函数实现的重载机制,这个函数名是operator+重载的运算符构成

1、哪些运算符不能重载?

(1)类成员访问符中指向->可以重载,.不能重载

(2)作用域访问符::不能重载

(3)三目运算符不能重载

(4)sizeof不能重载

2、运算符的重载规则?

(1)不能改变运算符的优先级和结合性

(2)不能改变运算符的用法

(3)不能创造新的运算符

(4)运算符重载函数中不允许有函数的默认值

二、迭代器

class String
{
private:
   char* mptr;
}
int main()
{
   String str;
   return 0;
}

上述代码如果想访问mptr中的数值,也就是指针所指向的数值该怎么访问?我们可以给一个指针类型,让指针类型直接指向mptr的位置,通过指针来访问,这样做是不行的,因为mptr是私有的,指针不能指向内部去访问,会破坏封装性。普通指针不能处理,我们可以通过面向对象的指针(面向对象的指针就是迭代器),这个指针指向对象的位置,容器提供两个接口,这两个接口是半开半闭的空间,一个是begin()迭代起始位置的元素,一个是end()迭代末尾位置后继位置的元素,让指针指向对象后,让指针由对象提供的begin接口给指针赋予起始的位置,让指针由对象提供的end接口给指针赋予末尾的位置。

找到了起始位置和末尾位置,如何通过当前位置找到下一个元素的位置呢?我们可以用++运算符的重载函数,这个运算符重载函数封装了每个容器找下一个位置的方式。

迭代器的核心思想:

  • 迭代器就是提供一个统一的容器遍历方式,迭代器就是用来遍历容器的。
  • 容器提供两个接口,这两个接口是半开半闭的空间,一个是begin()迭代起始位置的元素,一个是end()迭代末尾位置后继位置的元素,再提供一个next接口;由容器对象提供的begin接口让指针迭代到起始位置的元素,通过调用next(++)找到下一个元素来遍历元素,再通过解引用的方法获取元素,通过end保证了数据的结尾。

迭代器的优点:

1、避免了暴漏容器内部的可能

2、针对于不同数据结构提供了统一的容器遍历方式

三、写时如果要访问拷贝

下面三个拷贝用string类举例

1、浅拷贝

浅拷贝是一个简单的赋值,如果有指针存在,会让两个指针指向同一个内存块,当内存块需要被释放,会导致内存被重复释放,程序出现问题

缺点:对象销毁的时候同一内存可能会被多个对象同时释放,导致程序出现问题

优点:实现了数据共享,开销小,节省空间

2、深拷贝

深拷贝让每个对象都拥有自己独立的资源

缺点:资源占用率大,内存访问率低

3、写时拷贝:通过operator[]函数实现,比如str2[2]='b'是修改str2的2号下标的值

(1)写之前浅拷贝

内存的释放在最后一个对象销毁时释放,怎么判断最后一个对象?给一个计数器count,看该堆内有多少对象指向;这个计算器在内存中保存,因此内存由一个count区(四个字节),有一个data;每有一个新的对象生成,让这个对象指向内存,每增加一个对象,count+1;每释放一个对象count-1,直到count=0,再释放内存。

(2)写时深拷贝

第一种方式:普通方式

如果str2想要修改则需要str2对象和内存断开,count-1;str2重新开辟堆内存,再把数据拷贝过去,再做修改

第二种情况:内存上本来就只有一个对象指向

不需要开辟新的内存,直接修改

四、内存池 

实现自主的内存管理机制

new:

1、开辟内存operator new(开辟内存的函数)

2、调用构造函数(系统调用)

delete:

1、调用析构函数

2、释放内存operator delete(释放内存的函数)

设计内存管理机制需要自己重载operator new和operator delete,我们如果频繁的new和delete,效率会出现问题,如果内存比较小,也会出现内存碎片(外碎片),为了解决这两个问题,也能进行自主的内存管理,我们实现了内存池。

内存池的处理方式:

申请一块大的内存,以后的开辟和释放都是从内存块进行开辟,释放也释放给这个内存块;这个大的内存释放归还给系统后,系统可以把大的内存切割成小的内存继续分配,不会产生内存碎片;第一次申请是从操作系统申请的大内存,以后再申请相当于在应用程序段,这个时候内存本身就在用用程序断,不需要过内核,因此实现这个内存池可以解决频繁的new和频繁的delete存在的问题。

我们先开辟一个大的内存块,然后以静态链表的方式把这个内存块进行简单组织,也就是以数组的形式来实现链表的管理;我们左半边存储数据的数据区,右半边来做指向下一个结点的指针域,整个内存池只管理未被分配出去的结点。

1、首先我们先对内存池进行一个初始化,让第一个内存的指针域指向第二个内存单元,第二个内存单元的指针域指向下一个,以此类推,直到最后一个内存单元的指针域置为NULL。划分好之后,我们再用一个标志来标志它的起始位置。

2、当我们需要开辟一个内存的时候,从内存池分配一个对应的内存块,如果开辟的是整型的内存,就把这个内存块的数据域分配给整型使用,而指针域是不考虑的,因此整型可以使用的只有数据域;然后让这个标志指向下一个位置。

针对已经分配出去的内存,由外部变量进行管理,内存池管理未被分配出去的结点。

3、如果p不管理对应的堆内存了,要释放p的话,会释放给内存池,我们把p对应结点再插入到内存池,可以通过p这个结点对应的指针域指向poll指向的结点,poll的位置重新指向p结点

4、如果内存池的poll已经指向空,但依然需要开辟内存,我们就要实现扩容;我们可以再开辟一个大的内存池,让poll指向第一个结点的位置,但如果这个时候想释放p3,可以继续让p3的指针域指向poll所指向的结点,然后poll回到p3

内存池的思想:

池是内存资源的集合,它的管理方式就是从池中拿资源,接着归还给池,归换给池的资源也可以重新分配,保证了资源的循环利用。

考题:以前会让左边是数据域,右边是游标域(数组的下标),为什么现在右边是指针域?

因为现在下标已经不能代表唯一的内存块了,如果扩容就会有两个内存池,两个内存池的下标都是从0开始

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值