将C++类纳入存储池管理

        近日在做一个项目,遇到一个比较棘手的问题。程序在执行一次任务的时候会new大量的处理类和各种Buffer,在任务执行完成后,这些类和Buffer会被释放掉,但会产生一些内存碎片,当时项目时间比较紧凑,所以没有考虑内存管理这方面的事情。程序在多次执行任务后,在new一些较大块内存的时候会出现分配内存失败的异常,导致任务失败,但整体程序占用内存其实并不高。个人猜测可能是内存碎片过多导致。

        在处理问题的过程中,我想到,如果对内存进行统一管理,那就尽可能的减少了内存碎片的产生,降低了内存泄露的风险,如果以后有时间,还可以使用GC等内存管理技术来优化。

我们来做一些实验。

        首先,我们分配一块Buffer,接下来所有要分配的内存都从那里取得。如果是基本数据类型,比如char*,int*等,还好处理,直接存储即可,但是类就比较麻烦了,这就涉及到malloc和new的一个不同点:malloc只负责分配内存,new除了分配内存,还对类进行了初始化。

看如下代码

class A
{
public:
    A()
    {
        memcpy(m_s, "abcd\0", 5);
    }
    ~A() {};
    int GetA() { return m_a; }
    int SetA(int a) { m_a = a; }
private:
    int m_a = 5;
    char m_s[5];
};

int main()
{
    A* b = (A*)malloc(sizeof(A));
    A* a = new A;
    free(b);
    delete a
    return 0;
}

使用VS2019调试查看b 和 a的值

b中的值是无效的。如果我们直接将Buffer强转成类,效果跟使用malloc是一样的,无法对类成员进行初始化。

那么怎么对malloc出来的类成员进行初始化呢?

网上找到有几种做法:

第一种:根据类的内存布局直接赋值

int main()
{
	A* b = (A*)malloc(sizeof(A));
	A* a = new A;
	int n = 3;
	memcpy(((int*)b), &n, sizeof(int));
	memcpy(((char*)b + sizeof(int)), "bcde\0", 5);
	return 0;
}

 调试结果发现,可以初始化成员变量

 但这种方式有几个问题:第一,难以理解;第二,容易出错,必须得了解C++类的内存布局,万一类很复杂怎么办?万一有继承关系呢?

第二种:为每一个成员变量设置一个赋值函数

class A
{
public:
	A()
	{
		memcpy(m_s, "abcd\0", 5);
	}
	~A() {};

	int GetA() { return m_a; }
	void SetA(int a) { m_a = a; }

	void SetString(const char* s, size_t len)
	{
		memcpy(m_s, s, len);
	}
private:
	int m_a = 5;
	char m_s[5];
};

int main()
{
	A* b = (A*)malloc(sizeof(A));
	b->SetA(3);
	b->SetString("abcd\0", 5);
    free b;
	return 0;
}

结果如下:

这种比第一种方法可读,且安全性较高,是较为可行的一种方式。但使用起来有些不方便,如果成员变量有初始值也用不上,万一没有初始值会引起未定义行为呢?

第三种:先使用malloc分配,再使用 placement new 转换成指定类型

int main()
{
	void* t = malloc(sizeof(A));
	A* b = new(t)A;
	//A* b = (A*)malloc(sizeof(A));
	b->~A();
	return 0;
}

结果是正确的

说实话,这种方式我也是第一次见,结果是正确的,但用起来怪怪的,就不多说什么了。

还有没有其它方法呢?我们在在程序实现过程中,遇到一个问题,方法总比困难多。有些方法,或者是没掌握,或者是不知道,也许是一时半会儿没想到而已。在这里,我也琢磨出一种方法来:

unsigned char* g_pBuffer = new unsigned char[100];
size_t g_nPos = 0;

class A
{
public:
	A()
	{
		memcpy(m_s, "abcd\0", 5);
	}
	~A() {};
	void* operator new(size_t size)
	{
		if (size == 0)
		{
			return NULL;
		}
		unsigned char* b = &g_pBuffer[g_nPos];
		g_nPos += size;
		return b;
	}
	void operator delete(void* ptr)
	{
	}

	int GetA() { return m_a; }
	int SetA(int a) { m_a = a; }

private:
	int m_a = 5;
	char m_s[5];
};
int main()
{
	A* b = new A;
	delete[] g_pBuffer;
	return 0;
}

在这里,我使用了一个全局Buffer来做为存储池,要使用内存就从里面取。以前只是了解过operator new操作符,但没有在去认真用过。这次,我就想到用这种方式是否可以初始化类成员呢?结果是可行的

 分配成功,成员变量也初始化了,b对象还不用释放,用完直接删除g_pBuffer就可以了,感觉很好。

如果里面有复杂的对象呢,比如STL对象,来试试

class A
{
public:
	A()
	{
		memcpy(m_s, "abcd\0", 5);
	}
	~A() {};
	void* operator new(size_t size)
	{
		if (size == 0)
		{
			return nullptr;
		}
		unsigned char* b = &g_pBuffer[g_nPos];
		g_nPos += size;
		return b;
	}
	void operator delete(void* ptr)
	{
	}

	int GetA() { return m_a; }
	int SetA(int a) { m_a = a; }

private:
	int m_a = 5;
	char m_s[5];
	std::string m_str = "abcde";
	std::vector<int> m_v = { 123, 245, 366 };
};
int main()
{
	A* b = new A;
	delete[] g_pBuffer;
	g_nPos = 0;
	return 0;
}

结果也符合要求

 再看看g_pBuffer的内存布局

也行,vector自己会分配内存,所以暂时也想不到啥问题,实在有问题就用指针吧

如果有继承关系呢?

unsigned char* g_pBuffer = new unsigned char[100];
int g_nPos = 0;

class B
{
public:
	B()
	{
	}
	virtual ~B() {}
	virtual void SetB(int b)
	{
		m_b = b;
	}

private:
	int m_b = 7;
	std::string m_str = "CDEFG";
};

class A : public B
{
public:
    //与上面一致
    //.......
};

int main()
{
	memset(g_pBuffer, 0, 100);
	B* a = new A;
	delete[] g_pBuffer;
	g_nPos = 0;
	return 0;
}

结果如下

也运行得不错 。

当然,代码有些粗糙,肯定会有BUG,但这就是我想达到的效果。这里需要注意一点的是,如果要使用自己的存储池管理,要使用的类都必须重载new 和 delete,我们可以把重载函数放到一个基类里面去,要管理的类都从这个类继承,不就OK了?

class Base_Alloc
{
public:
	void* operator new(size_t size)
	{
		if (size == 0)
		{
			return nullptr;
		}
		unsigned char* b = &g_pBuffer[g_nPos];
		g_nPos += size;
		return b;
	}
	void operator delete(void* ptr)
	{
	}
};

class B : public Base_Alloc
{
    .....
}

这样,我就可以做到想要管理的类从存储池分配,然后统一管理。上面的代码仅仅是实验性质,如果要真正用到项目中,还有很多工作要做。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cantaloupe77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值