c++对象的动态地址重定位

visual Dynamic address relocation of C++ objects

本文关键字:定位 地址 动态 对象 c++      更新时间:2023-10-16

我想知道是否有可能使用动态基址而不是静态基址来处理类。基本思路如下:

对象A的定义如下:

class A
{
    //member variables
    ...
    //non-virtual member functions
    ...
    //virtual methods
    virtual void foo(...);
    ...
};

这个类不能实例化为堆栈对象,并且没有标准的new操作符。

相反,对象有一个location new,它将基址和基址的偏移量放入内存,并使用它来计算用于构造的绝对地址。

我想做的是让对象在代码中被访问,如下所示:

A* clsA=new(base,offset) A();
...
clsA->foo( ... );

,

(char*)clsA == (char*)(base+offset)

,还可以执行以下操作

base+=4;
...
clsA->foo( ... );

还有这个

(char*)clsA == (char*)(base+offset)

适用。

我不知道这在c++中是否可能。我知道它可以在ASM (x86/amd64)中完成,但我想要一个具有尽可能多的可移植性的解决方案(我承认仍然接近没有,但总比没有好)。有什么建议吗?

编辑:所以我想我不是很清楚我的问题。其思想是允许动态对象(在堆上分配的对象)在内存中移动。通常情况下,这不会是一个问题,但由于对象不能通过堆栈内存实例化,那么访问对象的唯一方法是通过指向对象底层内存的指针。当数组移动时(在这个例子中,移动了四个字节),从数组中借来的指针不再有效,需要更新。由于这个过程不仅会很长,而且会消耗比我希望的更多的内存,所以我希望能够让类在访问时重新计算它们的内存地址,而不是为每个借来的指针存储一个重定位表。

代表这个概念的程序集将是

;rax stores clsA 
mov rcx, rax 
shr rcx, 32 
mov DWORD PTR[rdx], rax 
lea rax, rdx+rcx 
push rax 
call foo

编辑2:事实证明,对于这种确切的行为类型,还有一个MSVC修饰符。__based声明了一个相对于另一个指针的指针,因此底层内存可以移动,指针仍然有效。

如果我理解你的话,你需要的是指针总是相对于另一个指针。这很简单,但通常是一个坏主意。

template<class T>
struct reloc_ptr {
    template<class U>
    reloc_ptr(char*& base, int offset, U&& v) 
        :base(&base), offset(offset) 
    {new(get())T(std::forward<U>(v));}
    T* get() const {return (T*)(*base+offset);}
    T* operator->() const {return get();}
    void destroy() {get()->~T();}
private:
    char** base;
    int offset;
};

然后正常用法是这样的

int main() {
    char* base = new char[1000];
    //construct
    reloc_ptr<A> clsA(base,4, "HI"); //I construct the A with the param "HI"
    //test
    clsA->foo();
    //cleanup
    clsA.destroy();
    delete[] base;
}

注意,reloc_ptr是相对于构建它的char* base的,所以非常非常小心。如果char* base变量在函数中,并且函数结束,那么用该char* base变量构造的所有指针都无效,并且使用它们会使程序做一些奇怪的事情。

http://ideone.com/4DNUGQ

与您所问的非常相似的是c++的位置新语法。

位置new在您不希望操作符new分配内存(您已经预先分配了内存,并且您希望将对象放置在那里),但您确实希望构造对象时使用。

在您的示例中,您可能会在内存中的特定位置分配类a,以便然后对其应用方法foo()。

void* memoryBuffer;
...
unsigned int i = 0;
for (uint i= 0; i < N; i += offsetSize){
   //initialize a given a specific location in memoryBuffer
   A* a = new(memoryBuffer + i)A(...); 
   //apply foo on that specific memory  location
   a->foo();
}

这就是使用Eigen将矩阵对象包裹在预分配的数值缓冲区周围的情况。

如果您正在谈论在偏移位置寻址类型为A *的对象,其他人以某种方式移动的对象,您将使用(clsA+offset)->foo( ... )之类的东西,其中offsetsizeof A单位中,即。你之前的4实际上是一个1,因为你可能意味着移动到内存中的"下一个"相邻对象。

如果你说的是实际移动对象,你可以在内存中的新地址上放置new,然后调用复制构造函数(或为POD调用memcpy),但这是相当不确定的,这取决于你的对象持有什么,如果它坚持指针的所有权,当你调用放置delete时,内存被释放,你很SOL。

我再怎么强调也不为过,告诉我们你想要完成什么因为可能有更好的方法,这与这里的谚语相悖