动态分配时将C++ 11对象添加到列表中?

And*_*zos 9 c++ c++11

假设我有一个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某种方式超载/覆盖?

asc*_*ler 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);
    }
};
Run Code Online (Sandbox Code Playgroud)

请注意,有时候V内存中的点元素还不存在或者不再存在X.用户可以绕过这些功能,但他们必须尝试.

如果你有另一个继承X但具有相同大小的类(所以没有其他子对象,除了可能是"空"基类),比如struct Y : public X {};,上面的代码会认为new Y是分配一个X.如果这是一个问题,您还需要添加operator newoperator void每个这样的类Y.我认为没有更通用的解决方案.


dyp*_*dyp 6

建立在aschepler的方法上,但现在使用虚拟基类重定向D的构造函数调用(不向向量添加实例).

主要思想是进行两步注册:首先,将任何调用operator new(无论是for X还是派生类)注册到unordered_set(X::dyn_alloc_set).然后,在构造时X,基于最派生类型进行选择,添加thisV是否已动态分配,以及它是否不是派生类.

必须从最派生类型调用虚拟基类的构造函数,因此可以使用它来区分构造期间DX构造期间.

#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)