arongnet 阅读(984) 评论(10)

一般而言,如果基类定义了operator new,那么派生类也必须对应定义。
考虑下面的两个类
char * pAddress;
class CBase
{
public:
 static void* operator new(size_t size){return pAddress;};
 static void operator delete(void * p){};
};

class CDerive:public CBase
{
 char buffer[1024];
public:
 CDerive()
 {
  for(int i=0; i < 1024; i ++)
  {
   buffer[i] = i %26 + 'a';
  }
 }
};
当调用CDerive * p = new CDerive时,编译器首先尝试匹配该类自己的new,由于没有,编译器就尝试匹配在其祖先链上的new,于是调用CBase::operator new
但是基类其实不知道派生类任何信息,它仅仅根据CBase处理,因此构造了一个错误的类对象。

下面是我的测试代码,你可以发现在delete时程序报告p2指针被破坏,这就是因为CDerive得不到自己的1024字节内容,因此覆盖了后面的内容

void test()
{
 char *p1, * p2;
 p1 = new char[10];
 memset(p1,0,10);
 pAddress = new char[sizeof(CBase)];
 p2 = new char[10];
 memset(p2,0,10);
 CDerive *pDerive = new CDerive;
 TRACE(_T("%p\n"),pDerive);
 delete p1;
 delete p2;
 delete pAddress;
 
}


评论列表
局部变量
我觉得这是
我觉得这是基类的new没有定义好,功能不完善导致的问题,并不能说明基类定义了new,派生类就也应该定义。
cjshaw0538
re: 基类定义了operator new,派生类有什么需求
首先谢谢大家对这个问题的解答呵呵

我问题的关键不是在new,而是在delete
我在一个freelist中维护了一个类及其子类的内存资源表
因为这个资源表中的对象大小不同,所以我要为其加个参数,标志其大小,以根据派生类以及基类的大小分配合适的节点。
这样,每次分配新节点的时候,因为在new(size_t)中可以知道待分配对象的确切大小,我就可以根据freelist中的size参数,获取一个正确的freelist节点,
关键是这个节点的size参数该如何设置?
我现在实现了这个东西,把size参数加在了基类里,对这个参数的设置有点麻烦,如果在delete(void*)中可以获取这个size的话,就不用弄得这么麻烦了
代码如下,初步看了一下,好像没有问题
但我想要更简便的设置参数的方法,在void operator delete(void*)中直接获取

class Base 
{
static Base* freelist; // pointer to free list
Base* next;
Base* previous;

protected:
size_t sz_;

public:
Base():sz_(sizeof(Base)), next(0), previous(0) {};
virtual ~Base() {};

virtual void SetSize(size_t sz) { sz_ = sz; };
virtual size_t GetSize() { return sz_; }

void* operator new(size_t sz)
{
//Base* p = 0;
if (freelist)
{
if ( sz == freelist->sz_ )
{
freelist->next->previous = freelist->previous;
//null == freelist->previous
//if (freelist->previous)
//{
// freelist->previous->next = freelist->next;
//}
freelist = freelist->next;
return freelist;
}

for (Base* temp=freelist->next; temp; temp=temp->next)
{
if ( sz == temp->sz_ )
{
/*p = temp;*/
// freelist = freelist->next;
if (temp->next)
{
temp->next->previous = temp->previous;
}

if (temp->previous)
{
temp->previous->next = temp->next;
}

return temp;
}
}
//p = freelist;
//freelist = freelist->next;
}

return new char[sz];
//  p->SetSize(sz); // 错误!未调用构造函数,不可调用虚函数

}
void operator delete(void* vp)
{
if (vp)
{
Base* p = (Base*)vp;

// link freed node onto freelist
if (freelist)
{
p->previous = freelist->previous;
freelist->previous = p;
}
else
{
p->previous = 0;
}
p->next = freelist;
freelist = p;
}
}
};

class Derived :public Base
{
int nPadding;

public:
Derived() { sz_ = sizeof(Derived); }
virtual ~Derived() {};
};
superbug
re: 基类定义了operator new,派生类有什么需求
还是有个地方有问题,改过来了,用在了TinyXML中
好像还好
if ( sz == freelist->sz_ )
{
if (freelist->next_)
freelist->next_->previous_ = freelist->previous_;

TiXmlBase* pTemp = freelist;
freelist = freelist->next_;
return pTemp;
}
superbug
re: 基类定义了operator new,派生类有什么需求
已知:
operator new和operator delete的其正规形式(normal signature):

void* operator new(size_t size);
void operator delete(void *p);
void operator delete(void *p,size_t size);

当然,void operator delete(void *p);的优先级比
void operator delete(void *p,size_t size);高一些。
但我需要void operator delete(void *p,size_t size);中的size参数,为此,我只重载
void* operator new(size_t size);
void operator delete(void *p,size_t size);
而不重载void operator delete(void *p);
这在VC6.0下会引起warning:没有与void* operator new(size_t size);匹配的delete,

在VS2005下可以无报警地安全通过编译。

请问,这是vc6.0编译器不标准的问题么?
标准C++对这方面是如何规定的?
清风雨
re: operator new
基类重载后,子类不是必须重载operator new的:
operator new只负责申请内存,size_t参数的大小是由编译器给定的:new Derive时编译器会指定正确的值,一般是sizeof(Derive)。
new operator调用operator new申请内存后会调用构造器。——这个工作也是编译器干的。

发表评论
切换编辑模式