建立在aschepler的方法上,但现在使用虚拟基类来重定向D
的构造函数调用(不向向量添加实例)。
主要思想是进行两步注册:首先,注册对 ( ) 的任何调用(operator new
无论是 forX
还是派生类)。然后,在构造时,根据最派生的类型进行选择,添加到它是否已被动态分配并且它不是派生类。unordered_set
X::dyn_alloc_set
X
this
V
必须从最派生的类型调用虚拟基类的构造函数,因此您可以使用它来区分构造之间D
和X
构造期间。
#include <unordered_set>
#include <typeinfo>
#include <vector>
#include <iostream>
#include <algorithm>
struct X;
std::vector<X*> V;
struct virt_base_class
{
friend struct X;
private:
virt_base_class(X* p); // this can only and will only be called by X
public:
virt_base_class() // this will be called by any class derived from X
{}
};
struct X
: protected virtual virt_base_class
{
private:
friend class virt_base_class;
static std::unordered_set<X*> dyn_alloc_set;
static bool dynamically_allocated(X* p)
{
return dyn_alloc_set.count(p) > 0;
}
public:
X()
: virt_base_class(this)
{}
static void* operator new(std::size_t size) {
void* p = ::operator new(size);
if (size == sizeof(X))
dyn_alloc_set.insert( static_cast<X*>(p) );
return p;
}
static void operator delete(void* p, std::size_t size) {
if (size == sizeof(X))
{
dyn_alloc_set.erase( static_cast<X*>(p) );
V.erase( std::remove(V.begin(), V.end(), static_cast<X*>(p)),
V.end() );
}
::operator delete(p);
}
};
virt_base_class::virt_base_class(X* p)
{
if( X::dynamically_allocated(p) )
V.push_back(p);
}
struct D : X
{}; // D::D will implicitly call virt_base_class::virt_base_class()
std::unordered_set<X*> X::dyn_alloc_set;
int main()
{
X x;
X* p = new X;
D d;
D* pd = new D;
std::cout << V.size();
}
更新:使用thread_local
存储来避免unordered_set
:
struct X
: protected virtual virt_base_class
{
private:
friend class virt_base_class;
static thread_local X* last_dyn_allocated;
static bool dynamically_allocated(X* p)
{
return p == last_dyn_allocated;
}
public:
X()
: virt_base_class(this)
{}
static void* operator new(std::size_t size) {
void* p = ::operator new(size);
if (size == sizeof(X))
{
last_dyn_allocated = static_cast<X*>(p);
}
return p;
}
static void operator delete(void* p, std::size_t size) {
if (size == sizeof(X))
{
X* pp = static_cast<X*>(p);
if(last_dyn_allocated == pp)
last_dyn_allocated = nullptr;
V.erase( std::remove(V.begin(), V.end(), pp),
V.end() );
}
::operator delete(p);
}
};
thread_local X* last_dyn_allocated = nullptr;