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

时间:2013-06-10 14:25:20

标签: c++ visual-c++ memory-management

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

让对象A定义如下:

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

此类无法实例化为堆栈对象,并且没有标准的新运算符。

相反,该对象有一个placement 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声明相对于另一个指针的指针,因此可以移动底层内存并且指针保持有效。这篇文章是 here

3 个答案:

答案 0 :(得分:1)

与您提出的问题非常相似的是C ++的placement new syntax

  

如果您不希望operator new分配内存(您已预先分配它并且想要将对象放在那里),则使用Placement 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附加在预分配的数字缓冲区周围包装矩阵对象的内容。

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

如果您正在谈论在偏移位置处理A *类型的对象,其他人以某种方式移动的对象,您会使用类似(clsA+offset)->foo( ... )的内容,其中offset是以sizeof A为单位,即。您之前的4实际上是1,因为您可能想要移动到内存中的“下一个”相邻对象。

如果您正在谈论实际移动对象,您可以将 - new放置在内存中的新地址,然后调用复制构造函数(或memcpy作为POD),但这是非常iffy取决于你的对象持有什么,如果它坚持指针的所有权和你调用放置 - delete内存得到释放你是非常SOL。

我不能强调这一点,告诉我们你想要完成什么,因为可能有一个更好的方法,这在很好地反对众所周知的谷物。