C++内存管理机制—Primitives笔记

Primitives -基本实体

C++语言中与内存相关的所有基础构件 (constructs),包括 malloc/free, new/delete,
operator new/operator delete, placement new/placement delete,探讨它们的意
义、运用方式和重载方式。并以此开发一个极小型内存池 (memory pool)。

1、c++应用程序,使用memoryde 途径

在这里插入图片描述

1.1 不同方式分配内存
在这里插入图片描述

不管什么方式在CRT都是使用的mallocfree

  void* p1 = malloc(512);	//512 bytes
    free(p1);

    complex<int>* p2 = new complex<int>; //one object
    delete p2;             

    void* p3 = ::operator new(512); //512 bytes
    ::operator delete(p3);

//以下使用 C++ 标準库提供的 allocators。
//其接口虽有标准规格,但实现厂商并未完全遵守;下面叁者形式略异。

#ifdef _MSC_VER
    //以下两个函数都是 non-static,定要通过 object 调用。以下分配 3 個 ints.
    int* p4 = allocator<int>().allocate(3, (int*)0); 
    allocator<int>().deallocate(p4,3);           
#endif
#ifdef __BORLANDC__
    //以下兩函數都是 non-static,定要通過 object 調用。以下分配 5 個 ints.
    int* p4 = allocator<int>().allocate(5);  
    allocator<int>().deallocate(p4,5);       
#endif
#ifdef __GNUC__
    //以下兩函數都是 static,可通過全名調用之。以下分配 512 bytes.
    //void* p4 = alloc::allocate(512); 
    //alloc::deallocate(p4,512);   
    
    //以下兩函數都是 non-static,定要通過 object 調用。以下分配 7 個 ints.    
	void* p4 = allocator<int>().allocate(7); 
    allocator<int>().deallocate((int*)p4,7);     
	
    //以下兩函數都是 non-static,定要通過 object 調用。以下分配 9 個 ints.	
	void* p5 = __gnu_cxx::__pool_alloc<int>().allocate(9); 
    __gnu_cxx::__pool_alloc<int>().deallocate((int*)p5,9);	
#endif

2 、基本构件之一 new/delete expressions

2.1 当用New分配空间时
  complex* pc = new complex(1, 2);

转化成编辑器
在这里插入图片描述

2.2 释放空间
delete pc;

编译器转化为
在这里插入图片描述

2.3 Ctor和Dotor的调用

在这里插入图片描述
在分配空间时调用构造函数和释放是调用析构函数的顺序是相反的。

3、基本构件之二 array new/delete

当数组类型分配连续的空间时。

complex * pca=new complex[3];//分配空间,调用了三次构造函数
.....
delete[] pca;//加上[] 调用了三次析构函数


string* psa=new string[3];
...
delete pca;//此时只调用了一次析构函数

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

如果是object类型,在delete时候必须加上[]否则会存在内存部分释放完的清空。

4、基本构件之三 placement new/delete

  • placement new允许我们将object构建于allocated memory中。
  • 没有所谓的placement delete,因为placement new根本没有分配memory。
  • placement是将前面分配的内存这给变量,没有重新分配内存。
char* buf = new char[sizeof(Complex)*3];
Complex* pc = new(buf)Complex(1,2);

编译器转化为

try
{
	void* mem = operator new(sizeof(Complex), buf);	// operator new()里面啥也没做,直接返回buf
	pc = static_case<Complex*>(mem);
	pc->Complex::Complex(1,2);
}
catch(std::bad_alloc)
{
	// 若allocation失败就不执行ctor
}

在这里插入图片描述

5、C++应用程序,分配内存的途径在这里插入图片描述

在这里插入图片描述

可以看到到最后面CRT都是使用的mallocfree

6、基本构件之重载

理解: 重载全局的operator new/opeartor delete,即类外重载 可以把分配空间和释放空间接管过来,自定义一些额外操作。

  • 我们可以重载class member operator new(),写出多个版本,前提是每一版本的声明都必须有独特的参数列,其中第一参数必须是size_t,其余参数以new所指定的placement arguments为初值。

在这里插入图片描述
事例

//定义一个Foo类,重载new和delete
class Foo
{
public:
  int _id;
  long _data;
  string _str;
  
public:
  	static void* operator new(size_t size);
  	static void  operator delete(void* deadObject, size_t size);
  	static void* operator new[](size_t size);
  	static void  operator delete[](void* deadObject, size_t size);	  	  
  
  Foo() : _id(0)      { cout << "default ctor. this="  << this << " id=" << _id << endl;  }
  Foo(int i) : _id(i) { cout << "ctor. this="  << this << " id=" << _id << endl;  }
  //virtual 
  ~Foo()              { cout << "dtor. this="  << this << " id=" << _id << endl;  }
  
  //不加 virtual dtor, sizeof = 12, new Foo[5] => operator new[]() 的 size 參數是 64, 
  //加了 virtual dtor, sizeof = 16, new Foo[5] => operator new[]() 的 size 參數是 84, 
  //上述二例,多出來的 4 可能就是個 size_t 欄位用來放置 array size. 
};

void* Foo::operator new(size_t size)
{
  	Foo* p = (Foo*)malloc(size);  
	cout << "Foo::operator new(), size=" << size << "\t  return: " << p << endl;  	

  	return p;
}

void Foo::operator delete(void* pdead, size_t size)
{
	cout << "Foo::operator delete(), pdead= " << pdead << "  size= " << size << endl;
	free(pdead);
}

void* Foo::operator new[](size_t size)
{
  	Foo* p = (Foo*)malloc(size);  //crash, 問題可能出在這兒 
	cout << "Foo::operator new[](), size=" << size << "\t  return: " << p << endl;  
	
  	return p;
}

void Foo::operator delete[](void* pdead, size_t size)
{
	cout << "Foo::operator delete[](), pdead= " << pdead << "  size= " << size << endl;
	
	free(pdead);
}

//方法调用
void test_overload_operator_new_and_array_new() 
{	
	cout << "\ntest_overload_operator_new_and_array_new().......... \n";		
	
	cout << "sizeof(Foo)= " << sizeof(Foo) << endl;
	
	{	
    Foo* p = new Foo(7);
    delete p;
    
    Foo* pArray = new Foo[5];	//無法給 array elements 以 initializer 
    delete [] pArray;	
	}
	
	{	    
	cout << "testing global expression ::new and ::new[] \n";
	// 這會繞過 overloaded new(), delete(), new[](), delete[]() 
	// 但當然 ctor, dtor 都會被正常呼叫.  
	
    Foo* p = ::new Foo(7);
    ::delete p;
    
    Foo* pArray = ::new Foo[5];	
    ::delete [] pArray;
	}
}

在这里插入图片描述

在这里插入图片描述
注意:

如果加上::,就会使用全局版本,通过自定义重载的部分。

7、Allocator 小型内存管理

分配器分配途径:allocator—>allocate—>::operator new—>malloc
我们想要把operator new / delete抽取出来形成单独的一个类allocator,是的这个类和内存分配的分配细节剥离开,这样,需要内存管理的类,就调用allocator。这就是STL中分配器的实现思路。
减少malloc调用次数,减少cookie。一次性性分配多个连续的空间。连续创建几个只有8个字节大小的对象,如果使用自己预先申请到的一大片内存的方式,那就没有cookie(但这大块内存的上下还是有cookie的),每个对象的地址间隔(一般,因为malloc拿到的内存可能是分散的)是8字节。
但是如果是没有使用自己的内存池,那么每个对象的地址间隔是16字节,在内存布局上是上下各多了4个字节的cookie。

7.1 v1版本

第一版本的opeartor new 和 operator delete:

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

class Screen {
public:
    Screen(int x) : i(x) { };
    int get() { return i; }

    void* operator new(size_t);
    void  operator delete(void*, size_t);	//(2)
//! void  operator delete(void*);			//(1) 二擇一. 若(1)(2)並存,會有很奇怪的報錯 (摸不著頭緒) 
	    
private:
    Screen* next;
    static Screen* freeStore;
    static const int screenChunk;
private:
    int i;
};
Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;

void* Screen::operator new(size_t size)
{
  Screen *p;
  if (!freeStore) {
      //linked list 是空的,所以攫取一大塊 memory
      //以下呼叫的是 global operator new
      size_t chunk = screenChunk * size;
      freeStore = p =
         reinterpret_cast<Screen*>(new char[chunk]);
      //將分配得來的一大塊 memory 當做 linked list 般小塊小塊串接起來
      for (; p != &freeStore[screenChunk-1]; ++p)
          p->next = p+1;
      p->next = 0;
  }
  p = freeStore;
  freeStore = freeStore->next;
  return p;
}


//! void Screen::operator delete(void *p)		//(1)
void Screen::operator delete(void *p, size_t)	//(2)二擇一 
{
  //將 deleted object 收回插入 free list 前端
  (static_cast<Screen*>(p))->next = freeStore;
  freeStore = static_cast<Screen*>(p);
}

//-------------
void test_per_class_allocator_1()
{	
	cout << "\ntest_per_class_allocator_1().......... \n";	
		
   	cout << sizeof(Screen) << endl;		//8	

size_t const N = 100;
Screen* p[N];	

   	for (int i=0; i< N; ++i)
   	     p[i] = new Screen(i);         

	//輸出前 10 個 pointers, 用以比較其間隔 
   	for (int i=0; i< 10; ++i)  	   
		cout << p[i] << endl;     
    
   	for (int i=0; i< N; ++i)
   	     delete p[i];     	
}
7.2 v2版本

使用了union ,用前4个字节当成指针来使用,即“embedded pointer”方法。
但是还是有个小缺点:收回来的指针全部累计起来了,如果能还给操作系统就更好了。
在这里插入图片描述
在这里插入图片描述

class Airplane {   //支援 customized memory management
private:
  	struct AirplaneRep {
    	unsigned long miles;
    	char type;
  	};
private:
  	union {
    	AirplaneRep rep;  //此針對 used object
    	Airplane* next;   //此針對 free list
  	};
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;
};

Airplane* Airplane::headOfFreeList;  
const int Airplane::BLOCK_SIZE = 512;   

void* Airplane::operator new(size_t size)
{
  	//如果大小錯誤,轉交給 ::operator new()
  	if (size != sizeof(Airplane))
    	return ::operator new(size);

  	Airplane* p = headOfFreeList;  

  	//如果 p 有效,就把list頭部移往下一個元素
  	if (p)
    	headOfFreeList = p->next;
  	else {
    	//free list 已空。配置一塊夠大記憶體,
    	//令足夠容納 BLOCK_SIZE 個 Airplanes
    	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; //以null結束

    	// 將 p 設至頭部,將 headOfFreeList 設至
    	// 下一個可被運用的小區塊。
    	p = newBlock;
    	headOfFreeList = &newBlock[1];
  	}
  	return p;
}

// operator delete 接獲一塊記憶體。
// 如果它的大小正確,就把它加到 free list 的前端
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 test_per_class_allocator_2() 
{	
	cout << "\ntest_per_class_allocator_2().......... \n";		
	
  	cout << sizeof(Airplane) << endl;    //8

size_t const N = 100;
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[9]->set(500000,'C');
  	cout << p[1] << ' ' << p[1]->getType() << ' ' << p[1]->getMiles() << endl;
  	cout << p[5] << ' ' << p[5]->getType() << ' ' << p[5]->getMiles() << endl;
  	cout << p[9] << ' ' << p[9]->getType() << ' ' << p[9]->getMiles() << endl; 
  	
	//輸出前 10 個 pointers, 用以比較其間隔 
   	for (int i=0; i< 10; ++i)  	   
		cout << p[i] << endl; 		 
	 
   	for (int i=0; i< N; ++i)
   	     delete p[i]; 	
}
7.3 v3版本 static allocator

特点:将内存的动作抽取到单一的class——allocator 中;在这里插入图片描述
在这里插入图片描述
效果:
在这里插入图片描述

7.4 v4版本 marco for static allocator

因为每个使用allocator 的类的几处写法是固定的,于是将它们写成宏:
在这里插入图片描述
在这里插入图片描述

7.5 global allocator(with multiple free-lists)

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值