动态内存


new的行为

void* operator new(size_t sz) throw(); 源码位置:libsupc++/new_op.cc/

2022-03-06_15-20-36.jpg

delete的行为

2022-03-06_15-25-53.jpg

重载全局new delete/new[] delete[]

全局的影响很远
new[]下面所占的大小要额外加上一个计数器。

new -> void* operator new(xxx)
new[] -> void* operator new[](xxxx)

重载成员new delete/new[] delete[]

可以构建内存池??
c++实现高效内存池

重载new()/delete() placement new

new (yyy) xxx; -> void* operator new(xxx, xxx, xxx);重载的函数
根据placement argument(也就是yyy)可以有多个版本。其中yyy是一个指针的比较重要。

只要当new所调用的ctor抛出exception,才会调用这些重载版本的delete().它只可能这样被调用,主要用来清除未能完全创建成功的object所站用的memory。

一种形式:

placementnew允许你在一个已经分配好的内存中(栈或堆中)构造一个新\color{blue}{placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新} 的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。

我们知道使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。placement new就可以解决这个问题。我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数;而且不会出现在程序运行中途出现内存不足的异常。所以,placement new非常适合那些对时间要求比较高,长时间运行不希望被打断的应用程序。

一篇讲的挺棒的博文

下面这是另一种形式:
2022-03-14_10-42-47.jpg

allocator类

将内存分配和对象构造分开。提供了一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。

new不灵活呗,不灵活体现在内存分配和对象构造是一起的。有啥不好的吗\color{red}{有啥不好的吗}

智能指针

不错的文章建议常看:
智能指针-使用 避坑和实现
c++智能指针最佳实践和源码分析

虽然对智能指针的概念比较熟悉了,但对具体的使用场景却不清楚。
主要是对所有权和生命周期的思考。
上面的一篇文章总结的不错:
unique_ptr使用场景:
简单的理解为:只在对象内部或方法内部使用的时候。
shared_ptr使用场景:
一般需要多个执行同一个对象的指针使用。
简单理解为:这个对象需要被多个class同时使用的时候。
weak_ptr使用场景
解决shared_ptr循环引用问题。

从面向对象的角度看:
组合关系- 在多线程中没问题,对象x的生命期由其唯一的拥有者控制。
比如,x是owner的直接数据成员,或者unique_ptr成员,或者owner持有的容器的元素。
关联关系-
聚合关系

有几个问题:
1.智能指针作为参数
智能指针作为参数,作为返回值,传递智能指针引用。

  • unique_ptr传递const by refence可以阻止在函数中释放unique_ptr,也就是说只能使用(读、写) 但不会误释放。
  • unique_ptr传值,没有拷贝赋值,需要move
  • shared_ptr传引用
void f1(const std::unique_ptr<std::string> &p)
{
    std::cout << "address: " << &p << " value: " << *p << "count: " 
                                        << std::endl;
    p = nullptr; //error
}

2.第三方库函数是裸指针但我使用的是智能指针??。

三种智能指针的API

#include <memory>

  • shared_ptr API
    1.解引用(*, ->)
    2.make_shared<T>(arg)
    3.sp.get()返回裸指针,最好不要用,就算要用也要确保不能delete
    4.sp.use_count()返回与p共享对象的智能指针个数,很慢
    5.sp.unique()是1返回true

  • unique_ptr API
    1.没有拷贝构造和拷贝赋值运算符,有一个例外那就是可以拷贝或赋值一个将要被销毁的unique_ptr(比如:从函数返回一个unique_ptr)
    2.析构器的型别是智能指针型别的一部分(shared_ptr不是,二者差异很大)
    3.up.release()up放弃对指针的所有权,返回指针,并将up置空。可以实现转移所有权到另一个unique_ptr
    4.up.reset(p)p==空 or p != 空
    5.解引用(*, ->)
    6.unique_ptr是容易转换成shared_ptr的,注意shared_ptr接受一个右值。

  • weak_ptr API
    一种不控制所指对象生命期的智能指针
    1.要用一个shared_ptr来初始化
    2.wp.use_count()与wp共享对象的shared_ptr的数量
    3.wp.reset()将wp置空
    4.由于对象可能不存在,不能直接通过weak_ptr直接访问对象,必须调用lock()。wp.lock():如果wp.use_cout() == 0返回空shared_ptr,否则返回一个指向wp的对象的shared_ptr
    5.无法解引用

使用智能指针中的注意事项

很重要,非常重要
见effective c++部分

shared_ptr

  • shared_ptr的尺寸是裸指针的两倍
  • 引用计数的内存必须动态分配
  • 引用计数的递增和递减是原子操作

源码分析

位置:shared_ptr以及weak_ptr -> include/tr1/shared_ptr.h
位置:unique_ptr -> include/bits/unique_ptr.h

shared_ptr和weak_ptr

2022-06-01_10-16-04.jpg