• 2.内存管理的四步优化


    主要讲述C++的内存分配的四步,即四个进化过程,不断封装的过程。
    从定义一个指针next,构建简单的单向链表;到使用union共同体,即将一个东西的前四个字节作为指针来使用(嵌入式指针);到将内存分配与回收封装成allocator类,该类中包含两个成员函数allocate和deallocate,在其它类需要内存管理时,只需要将allocator声明为类成员变量,然后调用allocate和deallocate即可;最后将需要内存管理类中的两个函数实现封装为macro(宏定义),这样更加的方便。

    内存管理第一步

    在类中定义一个指针,形成单向链表,减少malloc的次数,即也是减少cookie

    定义Screen类

    namespace per_class_allocator {
    
    	void per_class_allocator_function();
    	class Screen {
    	public:
    		Screen(int x) : i(x) {};
    		int get() { return i; }
    		void*  operator new(size_t);
    		void operator delete(void*, size_t);
    	private:
    		Screen * next;  //又多申请4个字节,膨胀百分百,降低malloc的次数,去除cookie,第一版多耗用一个next指针
    		static Screen* freeStore;
    		static const int screenChunk;
    	private:
    		int i;
    	};
    }
    

    重载Screen类的operator new和operator delete

    namespace per_class_allocator {
    	Screen* Screen::freeStore = 0;
    	const int Screen::screenChunk = 24;
    	
    	//重载Screen类的operator new
    	void* Screen::operator new(size_t size) {
    		Screen *p;
    		if (!freeStore) {
    			//linked list是空的,所以申请一大块
    			size_t chunk = screenChunk * size;
    			freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
    			//将一大块分割片片,当做linked list串接起来
    			for (; p = &freeStore[screenChunk - 1]; ++p)
    				p->next = p + 1;
    			p->next = 0;
    		}
    		p = freeStore;
    		freeStore = freeStore->next;
    		return p;
    	}
    
    	//重载Screen类的operator delete
    	void Screen::operator delete(void *p, size_t) {
    		//将deleted object插回free list前端
    		(static_cast<Screen*>(p))->next = freeStore;
    		freeStore = static_cast<Screen*>(p);
    	}
    
    

    开始测试

    	//开始测试
    	void per_class_allocator_function() {
    		std::cout << sizeof(Screen) << std::endl; //8
    		size_t const N = 10;
    		Screen* p[N];
    
    		for (int i = 0; i < N; ++i)
    			p[i] = new Screen(i);
    
    		//输出前10个pointers,比较其间隔
    		for (int i = 0; i < 10; ++i)
    			std::cout << p[i] << std::endl;
    
    		for (int i = 0; i < N; ++i)
    			delete p[i];
    	}
    

    内存管理第二步

    定义Airplane

    namespace per_class_allocator_two {
    	void per_class_allocator_two_function();
    
    	class Airplane {
    	private:
    		struct AirplaneRep {
    			unsigned long miles;
    			char type;
    		};
    	private:
    		union { 
    			AirplaneRep rep;
    			Airplane* next; //借用同一个东西的前四个字节作为指针来用
    		};
    	public:
    		unsigned long getMiles() { return rep.miles; }
    		char getType() { return rep.type; }
    		void set(unsigned long m, char t) {
    			rep.miles = m; 
    			rep.type = t;
    		}
    	public:
    		static void* operator new(size_t size);
    		static void operator delete(void* deadObject, size_t size);
    	private:
    		static const int BLOCK_SIZE;
    		static Airplane* headOfFreeList;
    	};
    }
    

    重载operator new和operator delete

    	Airplane* Airplane::headOfFreeList;
    	const int Airplane::BLOCK_SIZE = 512;
    	
    	//重载Airplane的operator new
    	void* Airplane::operator new(size_t size) {
    		if (size != sizeof(Airplane))
    			return ::operator new(size);
    		
    		Airplane* p = headOfFreeList;
    		if (p) //如果p有效,就把list的头部下移一个元素
    			headOfFreeList = p->next;
    		else {
    			//free list 已空,申请分配一个大块内存
    			Airplane* newBlock = static_cast<Airplane*>(::operator new(BLOCK_SIZE * sizeof(Airplane)));
    			
    			//再将每个小块串成一个free list,但是要跳过#0,它被传回,作为本次成果
    			for (int i = 1; i < BLOCK_SIZE - 1; ++i)
    				newBlock[i].next = &newBlock[i + 1];
    			newBlock[BLOCK_SIZE - 1].next = 0; //结束list
    			p = newBlock;
    			headOfFreeList = &newBlock[1];
    		}
    		return p;
    	}
    
    	//重载Airplane类的operator delete
    	//并没有还给操作系统,还是在这个自由链表中
    	void Airplane::operator delete(void* deadObject, size_t size) { 
    		if (deadObject == 0) return;
    		if (size != sizeof(Airplane)) {
    			::operator delete(deadObject);
    			return;
    		}
    		Airplane* carcass = static_cast<Airplane*>(deadObject);
    		carcass->next = headOfFreeList;
    		headOfFreeList = carcass;
    	}
    

    开始测试

    	void per_class_allocator_two_function() {
    		std::cout  << sizeof(Airplane) << std::endl;
    		size_t const N = 10;
    		Airplane* p[N];
    
    		for (int i = 0; i < N; ++i)
    			p[i] = new Airplane;
    		//随机测试object是否正常
    		p[1]->set(1000, 'A');
    		p[5]->set(2000, 'B');
    		p[8]->set(500000, 'C');
    
    		//输出10个pointers
    		for (int i = 0; i < N; ++i)
    			std::cout << p[i] << std::endl;
    		for (int i = 0; i < N; ++i)
    			delete p[i];
    	}
    

    测试结果

    8
    007312C8
    007312D0
    007312D8
    007312E0
    007312E8
    007312F0
    007312F8
    00731300
    00731308
    00731310
    请按任意键继续. . .
    

    内存管理第三步

    将所有的内存管理设计成一个类allocator,当在设计class时,不需要再重载operator new和operator delete.即分配特定尺寸的memory allocator概念封装起来。

    每个allocator object都是一个分配器,它内部维护一个free lists,不同的allocator object维护不同的free lists. 集中到一个allocator中。

    定义类allocator

    	//设计allocator1类去封装malloc和delete
    	class allocator1 {
    	private:
    		struct obj {
    			struct obj*next; //嵌入式指针 embedded pointer
    		};
    
    	public:
    		void* allocate(size_t);
    		void deallocate(void*, size_t);
    	private:
    		obj * freeStore = nullptr;
    		const int CHUNK = 5; //小一些以便观察
    	};
    

    定义分配内存和释放内存的函数allocate和deallocate

    	//allocate的实现 类似于new
    	void* allocator1::allocate(size_t size) {
    		obj* p;
    		if (!freeStore) {
    			
    			//linklist为空,申请一大块内存
    			size_t chunk = CHUNK * size;
    			freeStore = p = (obj*)malloc(chunk);
    			
    			//将分配得到的一大块当做linked list般,小块小块串接起来
    			for (int i = 0; i < CHUNK - 1; ++i) {
    				p->next = (obj*)((char*)p + size);
    				p = p->next;
    			}
    			p->next = nullptr; //last node
    		}
    		p = freeStore;
    		freeStore = freeStore->next;
    		return p;
    	}
    
    	//allocator1的deallocate的首先
    	void allocator1::deallocate(void*p, size_t size) {
    		//将*p 收回插入到free list
    		((obj*)p)->next = freeStore;
    		freeStore = (obj*)p;
    	}
    

    开始测试

    定义测试类Foo

    	//实现Foo类去测试上面的allocator1类
    	class Foo {
    	public:
    		long L;
    		std::string str;
    		static allocator1 myAlloc;
    	public:
    		Foo(long l) : L(l) {}
    		
    		//重载Foo类的operator new
    		static void* operator new(size_t size) {
    			return myAlloc.allocate(size);
    		}
    		//重载Foo类的operator delete
    		static void operator delete(void* pdead, size_t size) {
    			return myAlloc.deallocate(pdead, size);
    		}
    		
    	};
    

    定义测试函数

    	allocator1 Foo::myAlloc;
    
    	void static_allocator_function() {
    
    		
    		Foo *p[100]; //这里只是定义100个指针,并没有new分配内存
    		std::cout << "sizeof(Foo)= " << sizeof(Foo) << std::endl;
    
    		for (int i = 0; i < 23; ++i) {
    			p[i] = new Foo(i);
    			std::cout << p[i] << " " << p[i]->L << std::endl;
    		}
    		for (int i = 0; i < 23; ++i)
    			delete p[i];
    
    	}
    

    测试结果

    sizeof(Foo)= 32
    007112C8 0
    007112E8 1
    00711308 2
    00711328 3
    00711348 4
    00711398 5
    007113B8 6
    007113D8 7
    007113F8 8
    00711418 9
    00711468 10
    00711488 11
    007114A8 12
    007114C8 13
    007114E8 14
    00711D38 15
    00711D58 16
    00711D78 17
    00711D98 18
    00711DB8 19
    00711E08 20
    00711E28 21
    00711E48 22
    请按任意键继续. . .
    

    内存管理第四步:macro for static allocator

    将myAlloc转换为macro,只是为了偷懒,方便Foo直接调用。allocator类的定义和版本三完全一样,只不过是将Foo类中的下面两行抽出来定义为macro:

    	//重载Foo类的operator new
    		static void* operator new(size_t size) {
    			return myAlloc.allocate(size);
    		}
    		//重载Foo类的operator delete
    		static void operator delete(void* pdead, size_t size) {
    			return myAlloc.deallocate(pdead, size);
    		}
    

    macro 定义

    //将myAlloc.allocate转换为宏定义
    	//DECLARE_POOL_ALLOC --used in class definition
    	//用反斜杠来续行
    #define DECLARE_POOL_ALLOC() 
    public:
    	void* operator new(size_t size) { return myAlloc.allocate(size); } 
    	void operator delete(void* p) { myAlloc.deallocate(p, 0); } 
    protected: 
    	static allocator1 myAlloc;
    
    	//IMPLEMENT_POOL_ALLOC--used in class implementation file
    #define IMPLEMENT_POOL_ALLOC(class_name) 
    	allocator1 class_name::myAlloc;
    

    头文件中Foo的定义(用宏又封装了一层)

    	//定义用于测试的类 Foo
    	class Foo {
    		DECLARE_POOL_ALLOC()
    	public:
    		long L;
    		std::string str;
    	public:
    		Foo(long l) :L(l) {}
    	};
    

    cpp中的实现

    IMPLEMENT_POOL_ALLOC(Foo)
    
  • 相关阅读:
    扫描线 leetcode 759
    创建tensor的方法
    vector
    scatter_
    size、shape
    复杂问题
    random.normal
    set
    将可迭代对象中的元素通过字典映射成数字
    zipfile.ZipFile
  • 原文地址:https://www.cnblogs.com/ccpang/p/12195279.html
Copyright © 2020-2023  润新知