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

来源:互联网 发布:冷库软件 编辑:程序博客网 时间:2024/05/17 22:14

因为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>#endiftemplate <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_SIGNNEWstd::lock_guard<std::mutex> l(m_g_lock);#endifm_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_SIGNNEWstd::lock_guard<std::mutex> l(m_g_lock);#endifstd::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_SIGNNEWstatic std::mutex m_g_lock;#endifstatic std::map<NewRecord, size_t> m_g_recods;};#ifdef THREAD_SAFETY_SIGNNEWtemplate <typename T>std::mutex SignNew<T>::m_g_lock;#endiftemplate <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>#endiftemplate <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_SIGNNEWstd::lock_guard<std::mutex> l(m_g_lock);#endifm_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_SIGNNEWstd::lock_guard<std::mutex> l(m_g_lock);#endifstd::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_SIGNNEWstatic std::mutex m_g_lock;#endifstatic std::map<NewRecord, size_t> m_g_recods;};#ifdef THREAD_SAFETY_SIGNNEWtemplate <typename T>std::mutex SignNew<T>::m_g_lock;#endiftemplate <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;//assertOnlyNew<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>#endiftemplate <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_INHERTpublic:T& Impl() { return m_impl; }const T& Impl() const { return m_impl; }operator T() const { return m_impl; }#endifpublic: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_SIGNNEWstd::lock_guard<std::mutex> l(m_g_lock);#endifm_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_SIGNNEWstd::lock_guard<std::mutex> l(m_g_lock);#endifstd::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_INHERTprivate:T m_impl;#endifprivate:bool m_is_new = false;private:#ifdef THREAD_SAFETY_SIGNNEWstatic std::mutex m_g_lock;#endifstatic std::map<NewRecord, size_t> m_g_recods;};#ifdef THREAD_SAFETY_SIGNNEWtemplate <typename T>std::mutex SignNew<T>::m_g_lock;#endiftemplate <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_

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