c++ 内存管理二:重载(接管内存管理工具)

前言

重载 operator new 运算符来自定义内存分配的行为。重载 operator new 运算符允许我们使用自定义的内存分配逻辑,例如使用池分配、内存池等。

以下是重载 operator new 运算符的几种方式:

1 重载全局的 ::operator new 运算符

void* operator new(std::size_t size) {
    // 自定义内存分配逻辑
    void* ptr = /* 自定义逻辑 */;
    return ptr;
}

通过重载全局的 operator new,我们可以自定义所有类的内存分配行为,注意这里一般不会这样做,因为影响太深远,涉及面广,因为程序中可能大多数类的new都是使用默认的,没有重载自身类的operator new,可能都是使用全局的 ::operator new

2 重载类的 operator new 运算符

class MyClass {
public:
    static void* operator new(std::size_t size) {
        // 自定义内存分配逻辑
        void* ptr = /* 自定义逻辑 */;
        return ptr;
    }
};

在类中重载 operator new 运算符,可以针对该类自定义内存分配的行为。
因为new往往是在创建对象的时候,所以不是对象的方法,应该是static。

示例代码

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;  }
  ~Foo()              { cout << "dtor. this="  << this << " id=" << _id << endl;  }
};

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";
	// 直接调用全局的 ::new 这会绕过 overloaded new(), delete(), new[](), delete[]() 
	// 但 ctor, dtor 都会被正常调用.  
	
    Foo* p = ::new Foo(7);
    ::delete p;
    
    Foo* pArray = ::new Foo[5];	
    ::delete [] pArray;
	}
}

3 重载类的带有额外参数的 operator new 运算符

void* operator new(std::size_t size, AdditionalArgs args) {
    // 自定义内存分配逻辑,可以使用额外的参数
    void* ptr = /* 自定义逻辑 */;
    return ptr;
}

通过重载带有额外参数的 operator new,我们可以在内存分配过程中传递额外的参数,以便进行更复杂的内存管理。

在重载 operator new 运算符时,注意以下几点:

  • 返回值类型必须为 void*,表示分配的内存地址。
  • 第一个参数是 std::size_t size,表示要分配的内存大小。
  • 可以有额外的参数,以满足特定需求。
  • 可以使用任何自定义的内存分配逻辑,例如使用 malloc、内存池等。
  • 注意处理错误和异常情况,确保内存分配成功。

示例代码

class Foo
{
public:
  	Foo() { cout << "Foo::Foo()" << endl;  }
  	Foo(int) { 
	   			cout << "Foo::Foo(int)" << endl;  
			 }

  	//(1) 这个就是一般的 operator new() 的重载
  	void* operator new(size_t size) {
		cout << "operator new(size_t size), size= " << size << endl;
    	return malloc(size);  
  	}

  	//(2) 这个就是标准库已经提供的 placement new() 的重载 (形式)
  	void* operator new(size_t size, void* start) { 
	  	cout << "operator new(size_t size, void* start), size= " << size << "  start= " << start << endl;
    	return start;
  	}

  	void* operator new(size_t size, long extra) { 
	  	cout << "operator new(size_t size, long extra)  " << size << ' ' << extra << endl;
    	return malloc(size+extra);
  	}

  	void* operator new(size_t size, long extra, char init) { 
	  	cout << "operator new(size_t size, long extra, char init)  " << size << ' ' << extra << ' ' << init << endl;
    	return malloc(size+extra);
  	}
  	
  	//(1) 这是对应一般的 operator delete() 的重載 
  	void operator delete(void*,size_t)
  	{ cout << "operator delete(void*,size_t)  " << endl;  }

	//(2) 这是对应上述的 (2)  
  	void operator delete(void*,void*)
  	{ cout << "operator delete(void*,void*)  " << endl;  }

	//(3) 这是对应上述的 (3)  
  	void operator delete(void*,long)
  	{ cout << "operator delete(void*,long)  " << endl;  }

	//(4) 这是对应上述的 (4)  
	//如果沒有一一对应, 也不会有任何编译错误
  	void operator delete(void*,long,char)
  	{ cout << "operator delete(void*,long,char)  " << endl; }
  	
private:
  	int m_i;
};

void test_overload_placement_new()
{
	cout << "\n\n\ntest_overload_placement_new().......... \n";
	
  	Foo start;  //Foo::Foo

  	Foo* p1 = new Foo;           //op-new(size_t)
  	Foo* p2 = new (&start) Foo;  //op-new(size_t,void*)
  	Foo* p3 = new (100) Foo;     //op-new(size_t,long)
  	Foo* p4 = new (100,'a') Foo; //op-new(size_t,long,char)

  	Foo* p5 = new (100) Foo(1);     //op-new(size_t,long)  op-del(void*,long)
  	Foo* p6 = new (100,'a') Foo(1); //
  	Foo* p7 = new (&start) Foo(1);  //
  	Foo* p8 = new Foo(1);           //
  	
}

在这里插入图片描述

//VC6 warning C4291: ‘void *__cdecl Foo::operator new(unsigned int)’
//no matching operator delete found; memory will not be freed if
//initialization throws an exception

这表示对对应的new调用的构造函数的异常不做处理。所以这里delete参数实际上是对应异常的类型。

将构造函数抛出异常,然后打印。这里要注意将调用的代码即test_overload_placement_new使用try catch包含,否则无法捕获异常。

Foo(int) { 
   			cout << "Foo::Foo(int)" << endl;  
           	throw 1;
		 }

可以看到,operator delete(void*,long)是执行了。
当然异常抛出以后后面的自然也就不执行了。
在这里插入图片描述

4 单类重载new的优化示例

版本1:
需要指针指向下一个快的空间,因此并没有减少占用的内存
在这里插入图片描述
版本2:
内存管理一般是一个链表,对于每一个内存节点,必然需要一个next指针,但是这就算额外损耗了。Rep是我们需要存放的数据。
刚开始,内存块未被使用,因此该obj所占内存全被next指针拿去用了;但是当内存块被使用的时候(分配给Rep了),此时next所占空间可以看为0,所有内存空间都给data使用了,这就没有任何损耗。
在这里插入图片描述
版本3:
如果每个类都要重写一个new的方法,那么太过繁琐,因此需要将重复动作抽取出来放到一个class里面
抽取出来形成一个方法 。最终将每个类的动作封装成宏,使用更加方便。
在这里插入图片描述

5 总结

本章内存管理的关键:

1. 减少malloc调用次数,节约了cookie
2. 使用链表来链接内存块
3. 使用union嵌入式指针避免指针浪费

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值