从用户控制的内存池中分配和实例化对象

时间:2016-04-03 22:18:34

标签: c++ placement-new

  • 我使用的C ++编译器是旧版本(可能是C ++ 98?)
  • 我无法使用new,malloc
  • 之类的东西从系统池动态分配内存
  • 但是,我可以从我定义的堆数组中使用对malloc的内置OS调用。

当我执行以下操作时,我遇到了一些奇怪的行为(程序崩溃)

class cBaseClass /* pure abstract */
{
public:
virtual void ifFunc( void ) = 0;
virtual ~cBaseClass() = 0;
}

inline cBaseClass::~cBaseClass()
{
}

class cDclass:cBaseClass
{
public:
cDclass();
~cDclass();
void ifFunc( void ); /* implement the pure virtual */
}

cDclass::cDclass( void )
{
printf("[0x%X] derived constructor called\n", this);
}
cDclass::~cDclass( void )
{
printf("[0x%X] derived destructor called\n", this);
}
void cDclass::ifFunc(void)
{
 printf("[0x%X] ifFunc called from derived class\n", this);
}

uchar_t myHeap[4096];

int main ( void )
{
   cDclass* pMyPtr = NULL;
    uint32_t i = 0;
    ( void ) memset( myHeap, 0, sizeof(myHeap)/sizeof(myHeap[0]);
   for( i = 0; i < 20; i++)
   {
       pMyPtr = myHeap[i * sizeof(cDclass) + 4];
       *pMyPtr = cDclass();
       pMyPtr->ifFunc(); /* Crash */        
   }
}

我看到派生类的构造函数被调用..然后它的析构函数被调用然后崩溃。 我错误地认为* pMyPtr = cDclass()构造一个类,然后在pMyPtr指定的地址处创建该类的副本? 我说这是因为当我删除 pMyPtr = cDClass() 并创建一个虚拟变量来存储cDclass的实例,然后使用memmove它不再崩溃。

2 个答案:

答案 0 :(得分:0)

*pMyPtr = cDclass();

假设*pMyPtr处已存在有效对象并使用其赋值运算符。 (在典型的实现中,这可能不够好,因为它不会复制vptr。)

你需要的是

new(pMyPtr) cDclass;

在指定的内存位置调用构造函数。您需要#include <new>

答案 1 :(得分:0)

第一个奇怪的是这一行:

pMyPtr = myHeap[i * sizeof(cDclass) + 4];

无怨无悔地编译。它隐含地将uchar_t转换为cDclass*,而在没有reinterpret_cast的情况下这是不可能的。但可能是你的编译器在那里更宽松。无论如何,至少你在这里错过了&运营商。

第二个问题是,是的,你的假设是错误的。该行的作用是在堆栈上构造一个临时对象,然后假设在指针位置已经存在一个完全构造的对象,调用该对象的编译器生成的复制赋值运算符,将临时对象作为参数,然后销毁暂时的。

永远不会发生的事情是对象实际上是在内存中构造的。这意味着vptr永远不会被初始化,因此对虚函数的调用是空取消引用。

您需要做的是使用 placement-new 来就地构建对象。你的循环应如下所示:

int main ( void )
{
    uint32_t i = 0;
    ( void ) memset( myHeap, 0, sizeof(myHeap)/sizeof(myHeap[0]);
    for( i = 0; i < 20; i++)
    {
        // get the address to construct the object at
        uchar_t* pMyAddr = &myHeap[i * sizeof(cDclass) + 4];
        // construct a new object in-place at that address
        cDclass* pMyPtr = new (pMyAddr) cDclass();
        pMyPtr->ifFunc(); /* Don't crash */        
    }
}

请注意,您将负责为您创建的所有对象实际调用析构函数。