C++实现记录类型内存分配方式的包装类,使对象(或原始类型)只能通过new的方式实例化

因为c++new的实现的特殊性(对于非POD类型,new完成之后自动调用对象的构造函数),但是new调用构造并不是在可以重载的new操作符中完成的,这部分的流程通过编程的手段没办法改变(至少以我目前的能力来说),所以想要简单的在new的某个阶段记录新生成的对象来自于new操作是没法做到的。

现在提供一种略微复杂一点(其实也很简单)的方法来标记对象生成来自于new还是栈上申请。

思路:

1,将需要限制分配方式的类型包装成一个类,使之创建的时候一定会调用构造函数

2,重载新类型所有的operator new并调用默认的operator new,记录返回的指针信息

3,在构造函数中比较this指针和记录下来的指针,是否相同

4,如果相同则表示对象是new出来的,否则不是

两种实现方式:

方式1,成员引用方式,代码如下:

#ifndef _ONLYNEW_H_
#define _ONLYNEW_H_

#include <algorithm>
#include <map>
#include <assert.h>

#define THREAD_SAFETY_SIGNNEW

#ifdef THREAD_SAFETY_SIGNNEW
#include <mutex>
#endif

template <typename T>
class SignNew
{
private:
	struct NewRecord
	{
		NewRecord(size_t _ptr, size_t _persize, size_t _membcount) :ptr(_ptr), persize(_persize), membcount(_membcount) {}
		size_t ptr = 0;		//Use size_t instead of void *
		size_t persize = 0;
		size_t membcount = 0;
		bool operator<(const NewRecord& that) const
		{
			return ptr < that.ptr;
		}
		struct FindOp
		{
			FindOp(size_t _ptr) : ptr(_ptr) {}
			bool operator()(const std::pair<NewRecord, size_t>& rcd)
			{
				return (ptr - rcd.first.ptr >= 0) && ((ptr - rcd.first.ptr) % rcd.first.persize == 0) && ((ptr - rcd.first.ptr) / rcd.first.persize < rcd.first.membcount);
			}
			size_t ptr = 0;
		};
	};

public:
	template <typename... Args>
	SignNew(Args &&... args) :m_impl(std::forward<Args>(args)...)
	{
		SignNewPtr(this);
	}

public:
	T& Impl() { return m_impl; }
	const T& Impl() const { return m_impl; }
	operator T() const { return m_impl; }

public:
	bool IsNew() const { return m_is_new; }

public:
	void* operator new(size_t size)
	{
		void* p = ::operator new(size);
		RecordNewPtr(p, size);
		return p;
	}
	void *operator new[](size_t size)
	{
		void* p = ::operator new(size);
		RecordNewPtr(p, size);
		return p;
	}
		void operator delete(void *ptr)
	{
		return ::operator delete(ptr);
	}
	void operator delete[](void *ptr)
	{
		return ::operator delete(ptr);
	}

private:
	static void RecordNewPtr(void* ptr, size_t size)
	{
		size_t sizer = size / sizeof(SignNew<T>) * sizeof(SignNew<T>);
		size_t ptrr = (size_t)ptr; ptrr += size - sizer;
#ifdef THREAD_SAFETY_SIGNNEW
		std::lock_guard<std::mutex> l(m_g_lock);
#endif
		m_g_recods.insert(std::make_pair<NewRecord, size_t>(NewRecord(ptrr, sizeof(SignNew<T>), sizer / sizeof(SignNew<T>)), 0));
	}

	void SignNewPtr(void* ptr)
	{
#ifdef THREAD_SAFETY_SIGNNEW
		std::lock_guard<std::mutex> l(m_g_lock);
#endif
		std::map<NewRecord, size_t>::iterator it = std::find_if(m_g_recods.begin(), m_g_recods.end(), NewRecord::FindOp((size_t)ptr));
		if (it != m_g_recods.end())
		{
			m_is_new = true;
			++(it->second);
			if (it->second == it->first.membcount)
				m_g_recods.erase(it);
		}
	}

private:
	T m_impl;

private:
	bool m_is_new = false;
private:
#ifdef THREAD_SAFETY_SIGNNEW
	static std::mutex m_g_lock;
#endif
	static std::map<NewRecord, size_t> m_g_recods;
};

#ifdef THREAD_SAFETY_SIGNNEW
template <typename T>
std::mutex SignNew<T>::m_g_lock;
#endif

template <typename T>
std::map<typename SignNew<T>::NewRecord, size_t> SignNew<T>::m_g_recods;

template <typename T>
class OnlyNew : public SignNew<T>
{
public:
	template <typename... Args>
	OnlyNew(Args &&... args) :SignNew(std::forward<Args>(args)...)
	{
		assert(IsNew());
	}
};

#endif // _ONLYNEW_H_t

 

方式2,基类继承方式,代码如下:

#ifndef _ONLYNEW_H_
#define _ONLYNEW_H_

#include <algorithm>
#include <map>
#include <assert.h>

#define THREAD_SAFETY_SIGNNEW

#ifdef THREAD_SAFETY_SIGNNEW
#include <mutex>
#endif

template <typename T>
class SignNew : public T
{
private:
	struct NewRecord
	{
		NewRecord(size_t _ptr, size_t _persize, size_t _membcount) :ptr(_ptr), persize(_persize), membcount(_membcount) {}
		size_t ptr = 0;		//Use size_t instead of void *
		size_t persize = 0;
		size_t membcount = 0;
		bool operator<(const NewRecord& that) const
		{
			return ptr < that.ptr;
		}
		struct FindOp
		{
			FindOp(size_t _ptr) : ptr(_ptr) {}
			bool operator()(const std::pair<NewRecord, size_t>& rcd)
			{
				return (ptr - rcd.first.ptr >= 0) && ((ptr - rcd.first.ptr) % rcd.first.persize == 0) && ((ptr - rcd.first.ptr) / rcd.first.persize < rcd.first.membcount);
			}
			size_t ptr = 0;
		};
	};

public:
	template <typename... Args>
	SignNew(Args &&... args) : T(typename std::forward<Args>(args)...)
	{
		SignNewPtr(this);
	}

public:
	bool IsNew() const { return m_is_new; }

public:
	void* operator new(size_t size)
	{
		void* p = ::operator new(size);
		RecordNewPtr(p, size);
		return p;
	}
	void *operator new[](size_t size)
	{
		void* p = ::operator new(size);
		RecordNewPtr(p, size);
		return p;
	}
		void operator delete(void *ptr)
	{
		return ::operator delete(ptr);
	}
	void operator delete[](void *ptr)
	{
		return ::operator delete(ptr);
	}

private:
	static void RecordNewPtr(void* ptr, size_t size)
	{
		size_t sizer = size / sizeof(SignNew<T>) * sizeof(SignNew<T>);
		size_t ptrr = (size_t)ptr; ptrr += size - sizer;
#ifdef THREAD_SAFETY_SIGNNEW
		std::lock_guard<std::mutex> l(m_g_lock);
#endif
		m_g_recods.insert(std::make_pair<NewRecord, size_t>(NewRecord(ptrr, sizeof(SignNew<T>), sizer / sizeof(SignNew<T>)), 0));
	}

	void SignNewPtr(void* ptr)
	{
#ifdef THREAD_SAFETY_SIGNNEW
		std::lock_guard<std::mutex> l(m_g_lock);
#endif
		std::map<NewRecord, size_t>::iterator it = std::find_if(m_g_recods.begin(), m_g_recods.end(), NewRecord::FindOp((size_t)ptr));
		if (it != m_g_recods.end())
		{
			m_is_new = true;
			++(it->second);
			if (it->second == it->first.membcount)
				m_g_recods.erase(it);
		}
	}

private:
	bool m_is_new = false;
private:
#ifdef THREAD_SAFETY_SIGNNEW
	static std::mutex m_g_lock;
#endif
	static std::map<NewRecord, size_t> m_g_recods;
};

#ifdef THREAD_SAFETY_SIGNNEW
template <typename T>
std::mutex SignNew<T>::m_g_lock;
#endif

template <typename T>
std::map<typename SignNew<T>::NewRecord, size_t> SignNew<T>::m_g_recods;

template <typename T>
class OnlyNew : public SignNew<T>
{
public:
	template <typename... Args>
	OnlyNew(Args &&... args) :SignNew<T>(std::forward<Args>(args)...)
	{
		assert(IsNew());
	}
};

#endif // _ONLYNEW_H_

测试例程:

#include "OnlyNew.h"

class CLS
{
public:
	CLS() {}
	CLS(int _1) :m_1(_1) {}
	CLS(double _2) :m_2(_2) {}
	CLS(int _1, double _2) :m_1(_1), m_2(_2) {}
	CLS(const std::string& _3) :m_3(_3) {}
	CLS(int _1, double _2, const std::string& _3) :m_1(_1), m_2(_2), m_3(_3) {}

private:
	int m_1 = 0;
	double m_2 = 1.23;
	std::string m_3 = "abc";
};


void testonlynew()
{
	{
		SignNew<CLS> cls1("Hello");
		SignNew<CLS> cls2[3];
		SignNew<CLS>* cls3 = new SignNew<CLS>(365, 3.1415926, "Hello");
		delete cls3;
		SignNew<CLS>* cls4 = new SignNew<CLS>[4];
		delete[] cls4;
	}
	{
		OnlyNew<CLS> cls5;	//assert
		OnlyNew<CLS>* cls6 = new OnlyNew<CLS>;
		delete cls6;
		OnlyNew<CLS>* cls7 = new OnlyNew<CLS>[4];
		delete[] cls7;
	}
}

 

如果这个模板类只在单线程中使用,则可以不用锁,如果会用到多线程中,编译时请指定THREAD_SAFETY_SIGNNEW

 

两种方式各有优缺点:

方式1:

优点:可以支持pod类型,如int,double

缺点:需要通过Impl()函数获取被包装的真实对象,或者显示使用类型转换运算符,使代码不太美观

方式2:

优点:解决方式1的缺点

缺点:不支持方式1的优点

整合后的代码:通过编译预定义指定实现方式

#ifndef _ONLYNEW_H_
#define _ONLYNEW_H_

#include <algorithm>
#include <map>
#include <assert.h>

#define USE_INHERT
#define THREAD_SAFETY_SIGNNEW

#ifdef THREAD_SAFETY_SIGNNEW
#include <mutex>
#endif

template <typename T>
class SignNew
#ifdef USE_INHERT
	 : public T
#endif
{
private:
	struct NewRecord
	{
		NewRecord(size_t _ptr, size_t _persize, size_t _membcount) :ptr(_ptr), persize(_persize), membcount(_membcount) {}
		size_t ptr = 0;		//Use size_t instead of void *
		size_t persize = 0;
		size_t membcount = 0;
		bool operator<(const NewRecord& that) const
		{
			return ptr < that.ptr;
		}
		struct FindOp
		{
			FindOp(size_t _ptr) : ptr(_ptr) {}
			bool operator()(const std::pair<NewRecord, size_t>& rcd)
			{
				return (ptr - rcd.first.ptr >= 0) && ((ptr - rcd.first.ptr) % rcd.first.persize == 0) && ((ptr - rcd.first.ptr) / rcd.first.persize < rcd.first.membcount);
			}
			size_t ptr = 0;
		};
	};

public:
	template <typename... Args>
	SignNew(Args &&... args)
#ifdef USE_INHERT
 : T(std::forward<Args>(args)...)
#else
 : m_impl(std::forward<Args>(args)...)
#endif
	{
		SignNewPtr(this);
	}

#ifndef USE_INHERT
public:
	T& Impl() { return m_impl; }
	const T& Impl() const { return m_impl; }
	operator T() const { return m_impl; }
#endif

public:
	bool IsNew() const { return m_is_new; }

public:
	void* operator new(size_t size)
	{
		void* p = ::operator new(size);
		RecordNewPtr(p, size);
		return p;
	}
	void *operator new[](size_t size)
	{
		void* p = ::operator new(size);
		RecordNewPtr(p, size);
		return p;
	}
		void operator delete(void *ptr)
	{
		return ::operator delete(ptr);
	}
	void operator delete[](void *ptr)
	{
		return ::operator delete(ptr);
	}

private:
	static void RecordNewPtr(void* ptr, size_t size)
	{
		size_t sizer = size / sizeof(SignNew<T>) * sizeof(SignNew<T>);
		size_t ptrr = (size_t)ptr; ptrr += size - sizer;
#ifdef THREAD_SAFETY_SIGNNEW
		std::lock_guard<std::mutex> l(m_g_lock);
#endif
		m_g_recods.insert(std::make_pair<NewRecord, size_t>(NewRecord(ptrr, sizeof(SignNew<T>), sizer / sizeof(SignNew<T>)), 0));
	}

	void SignNewPtr(void* ptr)
	{
#ifdef THREAD_SAFETY_SIGNNEW
		std::lock_guard<std::mutex> l(m_g_lock);
#endif
		std::map<NewRecord, size_t>::iterator it = std::find_if(m_g_recods.begin(), m_g_recods.end(), NewRecord::FindOp((size_t)ptr));
		if (it != m_g_recods.end())
		{
			m_is_new = true;
			++(it->second);
			if (it->second == it->first.membcount)
				m_g_recods.erase(it);
		}
	}

#ifndef USE_INHERT
private:
	T m_impl;
#endif

private:
	bool m_is_new = false;
private:
#ifdef THREAD_SAFETY_SIGNNEW
	static std::mutex m_g_lock;
#endif
	static std::map<NewRecord, size_t> m_g_recods;
};

#ifdef THREAD_SAFETY_SIGNNEW
template <typename T>
std::mutex SignNew<T>::m_g_lock;
#endif

template <typename T>
std::map<typename SignNew<T>::NewRecord, size_t> SignNew<T>::m_g_recods;

template <typename T>
class OnlyNew : public SignNew<T>
{
public:
	template <typename... Args>
	OnlyNew(Args &&... args) :SignNew<T>(std::forward<Args>(args)...)
	{
		assert(IsNew());
	}
};

#endif // _ONLYNEW_H_

 

后续:

1. 可以用c++11的enable_if或者c++17的constexpr if达到两种方式完全整合,得到最完美的解决方案。

2. N天之后突然想起来,SignNewOnlyNew都需要对其拷贝,移动,赋值等函数做一些处理。

3. thread safe也可以通过将m_g_recods声明为thread_local来实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值