假设我有一个X类:
struct X
{
...
};
Run Code Online (Sandbox Code Playgroud)
我有一个全局向量V:
vector<X*> V;
Run Code Online (Sandbox Code Playgroud)
我想将V的新实例添加到V中,当且仅当它是动态分配的时(作为完整的大多数派生对象,而不是子对象):
int main()
{
X x; // not added to V
new X; // added to V
struct D : X {};
new D; // not added to V
}
Run Code Online (Sandbox Code Playgroud)
有办法做到这一点吗?也许是以operator new某种方式超载/覆盖?
struct X {
public:
static void* operator new(std::size_t size) {
void* p = ::operator new(size);
if (size == sizeof(X))
V.push_back(static_cast<X*>(p));
}
static void operator delete(void* p, std::size_t size) {
if (size == sizeof(X))
V.erase(std::remove(V.begin(), V.end(), p), V.end());
::operator delete(p, size);
}
};
Run Code Online (Sandbox Code Playgroud)
请注意,有时候V内存中的点元素还不存在或者不再存在X.用户可以绕过这些功能,但他们必须尝试.
如果你有另一个继承X但具有相同大小的类(所以没有其他子对象,除了可能是"空"基类),比如struct Y : public X {};,上面的代码会认为new Y是分配一个X.如果这是一个问题,您还需要添加operator new和operator void每个这样的类Y.我认为没有更通用的解决方案.
建立在aschepler的方法上,但现在使用虚拟基类重定向D的构造函数调用(不向向量添加实例).
主要思想是进行两步注册:首先,将任何调用operator new(无论是for X还是派生类)注册到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();
}
Run Code Online (Sandbox Code Playgroud)
更新:使用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;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
366 次 |
| 最近记录: |