假设我有一个X类:
struct X
{
...
};
我有一个全局向量V:
vector<X*> V;
当且仅当它被动态分配时(作为完整的大多数派生对象,而不是子对象),我想将新的X实例添加到V:
int main()
{
X x; // not added to V
new X; // added to V
struct D : X {};
new D; // not added to V
}
有没有办法做到这一点?也许是以某种方式超载/覆盖operator new
?
答案 0 :(得分:8)
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);
}
};
请注意,有些时候V
的元素指向内存尚未实际或不再实际为X
的内存。用户可以绕过这些功能,但他们必须尝试。
如果你有另一个继承X
但具有相同大小的类(所以没有其他子对象,除了可能是“空”基类),如struct Y : public X {};
,上面的代码会认为{{1} }正在分配new Y
。如果这是一个问题,您还需要为每个此类X
添加operator new
和operator void
。我认为没有更通用的解决方案。
答案 1 :(得分:6)
以aschepler的方法为基础,但现在使用虚拟基类重定向D
的构造函数调用(不向向量添加实例)。< / p>
主要想法是进行两步注册:首先,将operator new
(无论是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();
}
更新:使用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;
答案 2 :(得分:1)
我认为你想要的最好的近似是强制使用工厂来动态分配对象:
#include <algorithm>
#include <utility>
#include <vector>
class X {
static std::vector<X*> dynamic_xs;
static void* operator new(std::size_t size) {
return ::operator new(size);
}
public:
~X() {
auto end = std::end(dynamic_xs);
auto pos = std::find(std::begin(dynamic_xs), end, this);
if (pos != end) {
if (pos != --end) {
std::iter_swap(pos, end - 1);
}
dynamic_xs.pop_back();
}
}
template <typename... Args>
friend X* make_x(Args&&... args) {
X* p = new X(std::forward<Args>(args)...);
dynamic_xs.push_back(p);
return p;
}
};
std::vector<X*> X::dynamic_xs;
客户端可以实例化堆栈分配的X
,但动态分配会导致访问错误,因为operator new
为private
。