new的调用方式?

ayw215 2008-12-04 10:12:47

#include <iostream>
using namespace std;
void *operator new(size_t size,void * arg1, int arg2)
{
cout<<"new:size== "<<size
<<" arg1== "<<(void *)arg1
<<" arg2== "<<arg2<<endl;
return arg1;
}

int main()
{
void *p=(void *)0x1234;
int *ip=new(p,100) int;

cout<<"ip== "<<(void *)ip <<endl;

return 0;
}

output:
new:size== 4 arg1== 00001234 arg2== 100
ip== 00001234
Press any key to continue

我想问的是,调用new时,参数是怎么传递的?
谢谢~~~
...全文
190 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
visame 2008-12-05
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 ayw215 的回复:]
我是lz.
各位都理解错我要表达的意思了.
我是想问,
C/C++ codevoid*operatornew(size_t size,void*arg1,intarg2)
参数传递的时候咋按照这样调用呢?
和我们平时调用的很不一样
C/C++ codeint*ip=new(p,100)int;
第一个参数跑到后面去了,arg1,和arg2跑到new的括号后面了?

[/Quote]
这是语言的规定。
应该没有太多可以解释的
ayw215 2008-12-05
  • 打赏
  • 举报
回复
我是lz.
各位都理解错我要表达的意思了.
我是想问,

void *operator new(size_t size,void * arg1, int arg2)

参数传递的时候咋按照这样调用呢?
和我们平时调用的很不一样

int *ip=new(p,100) int;

第一个参数跑到后面去了,arg1,和arg2跑到new的括号后面了?
visame 2008-12-04
  • 打赏
  • 举报
回复
拨开自定义operator new与operator delete的迷雾

C++里允许用户通过自定义operator new的方式来更改new表达式的行为,这给了程序员定制内存管理方案的自由。但是享受这种自由的时候必须遵守一定的规范,具体可以参见《Effective C++ 2nd》的相关条款。本文补充解释一些特别容易引起误解的问题。

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);

普通的new与delete表达式在分配与释放内存时调用的就是它们。一般来说operator delete(void*)的优先级比operator delete(void*,size_t)要高,这意味着如果在同一空间(scope)定义了这两种形式的delete,拥有单一参数者优先被编译器选择。这一点在VC7.1中得到验证,不知其它编译器如何?

除了上面的正规形式外,我们还可以定义拥有更多参数的operator new和operator delete,只要保证前者的返回值和第一个参数分别是void*和size_t类型,而后者的分别是void和void*就行了。比如:

void* operator new(size_t size,const char* szFile,int nLine);
void operator delete(void *p,const char*,int);

表达式new("xxx",20) SomeClass实际上就是告诉编译器调用上面的多参数operator new来分配内存。但是不要依此类推出 delete("xxx",20) pObj,这是非法的。那么怎么才能调用到这个多参数的operator delete呢?实话告诉你,你没有这个权利。呵呵,别吃惊,容我慢慢解释。当两个operator new和operator delete有相等的参数个数,并且除了第一个参数之外其余参数的类型依次完全相同之时,我们称它们为一对匹配的operator new和operator delete。按照这个标准,上面两位就是匹配的一对了。在我们使用 SomeClass *pObj = new("xxx",20) SomeClass 于堆中构建一个对象的过程中,如果在执行SomeClass的构造函数时发生了异常,并且这个异常被捕获了,那么C++的异常处理机制就会自动用与被使用的operator new匹配的operator delete来释放内存(补充一点:在operator new中抛出异常不会导致这样的动作,因为系统认为这标志着内存分配失败)。编译期间编译器按照以下顺序寻找匹配者:首先在被构建对象类的类域中寻找,然后到父类域中,最后到全局域,此过程中一旦找到即停止搜寻并用它来生成正确的内存释放代码,如果没有找到,当发生上述异常情况时将不会有代码用来释放分配的内存,这就造成内存泄漏了。而如果一切正常,delete pObj 则总是会去调用operator delete的正规形式。现在明白了吧,多参数的operator delete不是给我们而是给系统调用的,它平常默默无闻,但在最危急的关头却能挺身而出,保证程序的健壮性。为了有个感性的认识,让我们看看下面的代码(试验环境是VC7.1):

#include <malloc.h>

struct Base
{
Base()
{
throw int(3);
}

~Base() {}

void* operator new( size_t nSize, const char*,int)
{
void* p = malloc( nSize );

return p;
}

void operator delete( void *p)
{
free(p);
}

void operator delete( void* p,const char*,int)
{
free( p );
}
};

#define NULL 0
#define new new(__FILE__, __LINE__)

int main( void )
{
Base* p = NULL;

try
{
p = new Base;

delete p;
}
catch(...)
{
}

return 0;
}

跟踪执行会发现:程序在 p = new Base 处抛出一个异常后马上跳去执行operator delete(void*,const char*,int)。注释掉Base构造函数中的throw int(3)重来一遍,则new成功,然后执行delete p,这时实际调用的是Base::operator delete(void*)。以上试验结果符合我们的预期。注意,operator new和operator delete是可以被继承和重定义的,那接下来就看看它们在继承体系中的表现。引进一个Base的派生类(代码加在#define NULL 0的前面):

struct Son : public Base
{
Son()
{
}

void* operator new( size_t nSize, const char*,int)
{
// class Son

void* p = malloc( nSize );
return p;
}

void operator delete( void *p)
{
// class Son
free(p);
}

void operator delete( void* p,const char*,int)
{
// class Son
free( p );
}
};
然后将main函数中的p = new Base改成p = new Son并且取消对Base()中的throw int(3)的注释,跟踪执行,发现这回new表达式调用的是Son重定义的operator new,抛出异常后也迅速进入了正确的operator delete,即Son重定义的多参数版本。一切都如所料,是吗?呵呵,别急着下结论,让我们把抛异常的语句注释掉再跑一次吧。很明显,有些不对劲。这次delete p没有如我们所愿去调用Son::operator delete(void*),而是找到了在Base中定义的版本。怎么回事?我愿意留一分钟让好奇的你仔细想想。

找到答案了吗?没错,罪魁祸首就是那愚蠢的Base析构函数声明。作为一个领导着派生类的基类,析构函数竟然不声明成virtual函数,这简直就是渎职。赶紧纠正,在~Base()前加上一个神圣的virtual,rebuild and run.....。谢天谢地,世界终于完美了。

可能你会疑惑,在没有给基类析构函数加virtual之前,当发生异常时C++为什么知道正确地调用派生类定义的多参数operator delete,而不是基类的?其实很简单,new一个对象时必须提供此对象的确切类型,所以编译器能够在编译期确定new表达式抛出异常后应该调用哪个类定义的operator delete。对于正常的delete p来说,如果p被声明为非基类类型的指针,编译器就会在编译时决定调用这种声明类型定义的operator delete(静态绑定),而如果p是某种基类类型指针,编译器就会聪明地把到底调用哪个类定义的operator delete留待运行期决定(动态绑定)。那么编译器如何判断p是否是基类指针呢?实际上它的根据就是p的声明类型中定义的析构函数,只有在析构函数是虚拟的情况下p才被看成基类指针。这就可以解释上面碰到的问题。当时p被声明为Base*,程序中它实际指向一个Son对象,但我们并没有把~Base()声明为虚拟的,所以编译器大胆地帮我们做了静态绑定,也即生成调用Base::operator delete(void*)的代码。不过千万不要以为所有编译器都会这样做,以上分析仅仅是基于VC7.1在本次试验中的表现。事实上在C++标准中,经由一个基类指针删除一个派生类对象,而此基类却有一个非虚拟的析构函数,结果未定义。明白了吧老兄,编译器在这种情况下没有生成引爆你电脑的代码已经算是相当客气与负责了。现在你该能够体会Scott Meyers劝你"总是让base class拥有virtual detructor"时的苦心吧。

至于数组版本的operator new[]和opeator delete[],情况一样。朋友们可以自己做试验确认一下。

最后要指出的是,试验代码中对operator new和operator delete的实现相当不规范,负责任的做法仍然请大家参考Scott Meyers的著作。

heeten 2008-12-04
  • 打赏
  • 举报
回复
size 是arg1的大小吧。
lc19890326 2008-12-04
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 zmlovelx 的回复:]
实参传形参
[/Quote]
他意思是第一个形参size为什么没有显示给出吧?
帅得不敢出门 2008-12-04
  • 打赏
  • 举报
回复
实参传形参
SearchLife 2008-12-04
  • 打赏
  • 举报
回复
mark
cba_v 2008-12-04
  • 打赏
  • 举报
回复
up
visame 2008-12-04
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 heeten 的回复:]
size 是arg1的大小吧。
[/Quote]
不对!是要new的数据类型的大小sizeof
visame 2008-12-04
  • 打赏
  • 举报
回复
看看下面这个你就明白了:
string *ps = new string("Hello, world!");
它完成与下面代码相似的功能:
  void *memory = operator new(sizeof(string)); // 为String对象得到未经处理的内存
  call string::string("Hello, world!") on *memory; // 调用构造函数初始化内存中的对象
  string *ps = static_cast<string*>(memory); // ps指针指向新的对象

第一个参数size_t是sizeof(XXX)
XXX就是你要new的类型。
比如你的例子中就是int,于是sizeof(int)====4
如果换成char类型就变成了sizeof(char)==1了
下面的就是char
输出为:
new:size== 1 arg1== 0x1234 arg2== 100
ip== 0x1234


1 #include <iostream>
2 using namespace std;
3 void *operator new(size_t size,void * arg1, int arg2)
4 {
5 cout<<"new:size== "<<size
6 <<" arg1== "<<(void *)arg1
7 <<" arg2== "<<arg2<<endl;
8 return arg1;
9 }
10
11 int main()
12 {
13 void *p=(void *)0x1234;
14 char *ip=new(p,100) char;
15
16 cout<<"ip== "<<(void *)ip <<endl;
17
18 return 0;
19 }
20

lc19890326 2008-12-04
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 heeten 的回复:]
size 是arg1的大小吧。
[/Quote]
arg1是地址 size是那个类型的大小
lc19890326 2008-12-04
  • 打赏
  • 举报
回复
4L你找的那个资料我才看了的 跟LZ的问题不相关吧,那个主要是说delete和new两个匹配重载的问题啊
lc19890326 2008-12-04
  • 打赏
  • 举报
回复
LZ注意区分operator new和new operator的概念

你在主函数中使用的那个new是new operator,这个是C++的关键字,是不能重载的
他的执行包括三步:1. 调用operator new分配内存 ;2. 调用构造函数生成类对象;3. 返回相应指针。

你重载的那个new是opeartor new(当然是只有运算符可以重载啦,你听说过重载关键字么),在arg1地址处创建arg2个大小为size的对象,这种应该叫placement new,运行在已经分配好的内存处创建对象(arg1就是指向了这个已经分配的内存)。

重载operator new的时候第一个参数要保证是size_t,这个参数会根据你使用new operator后面的那个类型(比如new int)来给定(当然是size=sizeof(类型))。后面的参数就需要在使用new operator的时候给出了(即你写的new(p,100))。

不知道我的回答对了多少 其实我对new的相关问题也不是特别清楚,才也查了很多资料,LZ参考参考吧

65,211

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧