2021-04-28 智能指针与new

c++ 关键字new与智能指针总结

new 和 指针

  1. new的时候内存耗尽:
    使用placement new禁止new抛出异常
    即 int *p = new (nothrow) int;
    delete之后,记得将指针置为空。new分配内存并且会初始化,这和malloc不一样,malloc只负责分配指定大小的内存空间,所以尽可能不好混用C/C++。

  2. shared_ptr

    • 使用new得到分配的指针后直接传入shared_ptr进行管理,推荐使用make_shared(parm)模板函数,智能指针与普通指针不能混用。
    • shared_ptr可以自由的copy,assignment, 这是其他类型智能指针不具有的。
    • shared_ptr构造函数是显式地.这意味着: shared_prt p = new int(1024) 这是不合法的。
    • 异常发生时,所有对象都会被析构,由于C++不能同时有两个未处理的异常,因此析构函数不能抛出异常。
    • 使用智能指针,能够保证在函数的任意出口,资源都能够被正确的释放。资源获取即初始化 RAII。
  3. unique_ptr

  • 这是完全独占一个指针资源,不能够使用copy,赋值.所以只能用构造函数构造(移动构造也行,move).
  • 使用release将当前的指针返回,并置空自己.因此release通常返回给另一个智能指针.
  • 使用reset释放资源
  • 定义删除器时,是要在模板里面显式说明其类型,就像pq一样. P419
  1. weak_ptr
  • 工具类,防止shared_ptr死锁用的.

new 分配数组

  • eg: new int[10]后
    • 跟 (), 表示用0进行 值初始化 这里括号内不能加值, 不同于new单个元素,这很合理。
    • 什么都没有, 默认初始化,值未定义。
    • 使用C++11 {}初始化器,类似列表初始化。
  • 分配一个元素都没有的空数组是合法的,没有啥用的小知识。
  • 动态数组使用unique_ptr管理 unique_ptr<int[]> up(new int[10])

allocator类 memory.h

为了提供更低层级的控制,而不是new一样直接把分配和构造全部完成了。
分配可以用malloc,但是构造呢?好像C++没怎么用过构造的Api,但其实是有这个功能的,placement New,new (address) (type) initializer

new 与 operator new:
new是关键字,operator new 是运算符,每个类都可以重载。
new 调用operator new 分配内存空间,然后再初始化。new返回的指针,可能与operator new返回指针有一个偏移,以记录数组长度,以及对齐。比如长为3的数组可能内存中看起来是[3][0,0,0]

将分配与构造分离

  • 如果不分离,没有默认构造函数的类,就不能动态分配数组
  • allocator类是模板,allocator alloc
    • 调用allocate分配n个string大小的空间
    • 调用construct(p, args)在p处构造,可变参数模板,args参数会被完美转发到构造函数。这其实在Python中一句话的事情,cpp却要引入完美转发等比较麻烦的概念
    • 使用destroy p调用析构函数
    • 使用 deallocate(p,n)释放内存 n必须等于数组原来的大小
    • 其他区域填充算法,《cpp prime 5》 P429

关于内存分配,这些分配操作是真的分配了内存吗?
不是的,无论是operator new还是malloc,都是在虚拟地址空间里面分配连续的block,只有在cpu访问这些内存的时候,才会被真正分配物理内存。其实malloc是自己管理一片地址空间的,自己在相应数据结构上操作表示分配和释放。

Simplistically malloc and free work like this:

malloc provides access to a process’s heap. The heap is a construct in the C core library (commonly libc) that allows objects to obtain exclusive access to some space on the process’s heap.
malloc提供访问heap的接口,C core lib允许对象唯一的占用一部分heap空间。
Each allocation on the heap is called a heap cell. This typically consists of a header that hold information on the size of the cell as well as a pointer to the next heap cell. This makes a heap effectively a linked list.
每次分配都是在heap加入一个heap cell,cell包含一个头,表示这段cell大小。heap的结构是一个linked list。
When one starts a process, the heap contains a single cell that contains all the heap space assigned on startup. This cell exists on the heap’s free list.
当开启一个进程的时候,heap包含一个cell,包含其heap地址空间。注:这里最好回顾一下进程地址空间,虚拟地址空间的概念。
linux进程地址空间
When one calls malloc, memory is taken from the large heap cell, which is returned by malloc. The rest is formed into a new heap cell that consists of all the rest of the memory.
当call一次malloc的时候,从这个大的cell上面切割一块返回。注意,这只是更改了数据结构,并不会真正在主存储器上占用那么大的内存,只用cpu要访问这段地址的时候,才会去调度。才会从虚拟地址空间到物理地址空间。
When one frees memory, the heap cell is added to the end of the heap’s free list. Subsequent malloc’s walk the free list looking for a cell of suitable size.
当释放的时候,被释放的空间会被放到freelist上。
As can be expected the heap can get fragmented and the heap manager may from time to time, try to merge adjacent heap cells.
heap随着分配和释放,其地址会变得破碎。
When there is no memory left on the free list for a desired allocation, malloc calls brk or sbrk which are the system calls requesting more memory pages from the operating system.
当freelist空间不能放下malloc请求的大小时,会通过系统调用增加进程heap的地址空间。
Now there are a few modification to optimize heap operations.

For large memory allocations (typically > 512 bytes, the heap manager may go straight to the OS and allocate a full memory page.
The heap may specify a minimum size of allocation to prevent large amounts of fragmentation.
The heap may also divide itself into bins one for small allocations and one for larger allocations to make larger allocations quicker.
There are also clever mechanisms for optimizing multi-threaded heap allocation.
The more interestin
g part is how free works (and in this direction, malloc too can be understood better).

In many malloc/free implementations, free does normally not return the memory to the operating system (or at least only in rare cases). The reason is that you will get gaps in your heap and thus it can happen, that you just finish off your 2 or 4 GB of virtual memory with gaps. This should be avoided, since as soon as the virtual memory is finished, you will be in really big trouble. The other reason is, that the OS can only handle memory chunks that are of a specific size and alignment. To be specific: Normally the OS can only handle blocks that the virtual memory manager can handle (most often multiples of 512 bytes e.g. 4KB).

So returning 40 Bytes to the OS will just not work. So what does free do?

Free will put the memory block in its own free block list. Normally it also tries to meld together adjacent blocks in the address space. The free block list is just a circular list of memory chunks which have some administrative data in the beginning. This is also the reason why managing very small memory elements with the standard malloc/free is not efficient. Every memory chunk needs additional data and with smaller sizes more fragmentation happens.

The free-list is also the first place that malloc looks at when a new chunk of memory is needed. It is scanned before it calls for new memory from the OS. When a chunk is found that is bigger than the needed memory, it is divided into two parts. One is returned to caller, the other is put back into the free list.

There are many different optimizations to this standard behaviour (for example for small chunks of memory). But since malloc and free must be so universal, the standard behaviour is always the fallback when alternatives are not usable. There are also optimizations in handling the free-list — for example storing the chunks in lists sorted by sizes. But all optimizations also have their own limitations.

Why does your code crash:

The reason is that by writing 9 chars (don’t forget the trailing null byte) into an area sized for 4 chars, you will probably overwrite the administrative-data stored for another chunk of memory that resides “behind” your chunk of data (since this data is most often stored “in front” of the memory chunks). When free then tries to put your chunk into the free list, it can touch this administrative-data and therefore stumble over an overwritten pointer. This will crash the system.

This is a rather graceful behaviour. I have also seen situations where a runaway pointer somewhere has overwritten data in the memory-free-list and the system did not immediately crash but some subroutines later. Even in a system of medium complexity such problems can be really, really hard to debug! In the one case I was involved, it took us (a larger group of developers) several days to find the reason of the crash – since it was in a totally different location than the one indicated by the memory dump. It is like a time-bomb. You know, your next “free” or “malloc” will crash, but you don’t know why!

Those are some of the worst C/C++ problems, and one reason why pointers can be so problematic.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值