因为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天之后突然想起来,SignNew和OnlyNew都需要对其拷贝,移动,赋值等函数做一些处理。
3. thread safe也可以通过将m_g_recods声明为thread_local来实现。