• C++ 内存管理 - primitives - 侯捷


    primitives

    在这里插入图片描述

    分配释放属于是否可重载
    malloc()free()C不可
    newdeleteC++表达式不可
    ::operator new()::operator delete()C++函数
    allocator::allocate()allocator::deallocate()C++标准库可自由设计搭配容器

    new 和 delete

    C/C++中的new和delete的实现过程

    在这里插入图片描述
    operator new():第一个参数表示大小,第二个参数表示保证这个函数不抛出异常。
    在这里插入图片描述
    注意:构造函数不能直接调用,而析构函数可以直接调用。

    new[] 与 delete[] 要搭配使用,未搭配使用可能会内存泄漏,泄露的是指针所指向的地方。

    析构的时候次序会逆反。
    在这里插入图片描述
    对于 析构函数 没有意义,new[] 是否对应 delete[] 不重要,但是 当析构函数有意义时,必须对应。

    placement new

    placement new 允许我们将对象建构在一个已经分配的内存中。并且没有对应的placement delete。
    在这里插入图片描述
    第一部分,本来是分配内存,现在已经分配好了,所以直接返回。

    重载 operator new

    ::operator new 和::operator delete 全部可以重载,但是不推荐。类中 operator new 也可以重载,实现所需功能,这是最常用的。

    class Foo{
    public:
    	void *operator new(size_t);
    	void operator delete(void*,size_t);
    	//....
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意,可以写出多个版本,前提是每一个版本的声明都必须有独特的参数列。(第一参数必须是size_t)只有new所调用的ctor 抛出异常,才会调用这些重载版的 operator delete()。

    即使 operator delete() 未能一一对应operator new() 也不会出现任何报错。换句话说:放弃处理构造函数抛出的异常。

    per-class allocator

    一:
    在这里插入图片描述
    想利用类内重载operator new去接管内存的分配,然后利用内存池的观念【即创建出一大段连续空间的内存,然后将其切割成一小段一小段】,将创建的元素对象放在内存池切分好的各分段小内存片中,这样避免了多次调用new而造成生成多个带有cookie的内存空间。通过内存池的观念,可以生成一大段只带有两个头尾cookie的内存空间,而该一大段内存空间又被切分成每一小段的内存空间,且其中的每一小段内存空间片都可以共享这一整体的cookie信息。

    因为为了能将一大段内存空间切分成一小段一小段,然后通过单向链表的形式串接起来,所以必须多引入一个Screen* next指针。但这又会增加class Screen的大小【增加了4字节】。

    二:第一个占用了一个指针,浪费空间
    在这里插入图片描述
    这个版本通过union关键字来减少使用next而所占耗的内存!

    注意:上面两个版本的operator delete操作都没有将内存空间回收还回给系统,而是仍然存在的。虽然operator delete操作没有将这些分配的内存空间释放掉,但其仍在控制中即仍可继续重新使用【只不过freeStore或headOfFreeList此时又重新跑回整个一大块内存空间的头端】,所以不算内存泄漏!

    三:上面的版本不具有复用性

    将分配特定尺寸区块的memory allocator包装成一个class allocator,这样每个allocator object都是个分配器,体内维护一个free lists,不同类(如下面的class Foo,class Goo等) 里面调用生成的各自allocator objects维护不同的free lists。

    在这里插入图片描述
    由上知,class Foo或class Goo其operator new或operator delete最终调用的都是allocator object所维护的free list而进行操作的!

    另外,class Foo或class Goo中,应注意到:
    static allocator myAlloc,即myAlloc必须是个静态成员变量,且在类外定义(赋初值)。如果其不是静态成员变量时,则在构建class Foo类对象时,是没办法调用到myAlloc的。因为非静态成员变量只能通过对象调用【但此时Foo对象还没生成又如何调用!!】。而myAlloc又是用来生成Foo类对象的,所以得通过类名调用即应设置为static类型。

    在这里插入图片描述
    而class allocator的如下:

    class allocator{
    private:
    	struct obj{
    		struct obj* next;
    	};
    public:
    	static void* allocate(size_t);
    	static void deallocate(void*, size_t);
    private:
    	obj* freeStore = nullptr;
    	const int CHUNK = 5; // 标准库一般设置为20
    };
    void* allocator::allocate(size_t size){
    	obj* p;
    	if(!freeStore){
    		// linked list为空,则申请一大块
    		size_t chunk = CHUNK * size;
    		freeStore = p =(obj*)malloc(chunk); // 这里直接调用malloc进行分配空间
    		
    		// 将分配的一大块切分成5小段,并串接起来
    		for(int i = 0; i < (CHUNK - 1); ++i){
    			p->next = (obj*)((char*)p + size);
    			p = p->next;
    			// 上面这两步相当于p->next = p + 1,
    			// 只不过这里需要适应不同的类下的操作,因而设置成这种形式!!
    		}
    		p->next = nullptr; // 最后一小段的下一个位置指向空
    	}
    	p = freeStore;
    	freeStore = freeStore->next;
    	return p;
    }
    void allocator::deallocate(void* p, size_t){
    	// 将要删除的*p的位置调整为free list的头端
    	((obj*)p)->next = freeStore;
    	freeStore = (obj*)p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    四、macro for static allocator(per-class allocator 4)

    在这里插入图片描述
    由第三版本知,其黄色部分我们想将其定义为宏操作,进一步简化代码内容:

    // DECLARE_POOL_ALLOC  used in class definition
    #define DECLARE_POOL_ALLOC()\
    public:\
    	void* operator new(size_t size){
    		return myAlloc.cllocate(size);
    	}\
    	void operator delete(void* p){
    		myAlloc.deallocate(p, 0);
    	}\
    protected:\
    	static allocator myAlloc;
    
    // IMPLEMENT_POOL_ALLOC   used in class implementation file
    #define IMPLEMENT_POOL_ALLOC(class_name)\
    	allocator class_name::myAlloc;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用实例如图所示:
    在这里插入图片描述
    在类内进行宏声明,在类外进行宏定义【告诉编译器传入参数的class type】

    New Handler

    在这里插入图片描述
    在这里插入图片描述

    =default,=delete

    一个是需要默认版本,另一个是这个函数我不要。

    这两个函数不仅使用构造,同时适用于 operator new 和 operator delete。

    在这里插入图片描述

  • 相关阅读:
    MySQL事务并发问题和MVCC机制
    Docker常见命令使用
    Python数据结构: 列表(List)详解
    c++入门(1)
    安装k8s
    构造、清理、拷贝和移动简单实例
    第77题. 组合
    Zero-Shot 使用简单两层网络不用训练就能进行图像恢复
    驱动开发,stm32mp157a开发板的led灯控制实验(再优化),使用ioctl函数,通过字符设备驱动分步注册方式编写LED驱动,完成设备文件和设备的绑定
    Waymo数据集解析环境安装以及解析结果
  • 原文地址:https://blog.csdn.net/qq_43142509/article/details/125465186