当我执行以下操作时,我遇到了一些奇怪的行为(程序崩溃)
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它不再崩溃。
答案 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 */
}
}
请注意,您将负责为您创建的所有对象实际调用析构函数。