`
yanfaguanli
  • 浏览: 660488 次
文章分类
社区版块
存档分类
最新评论

new表达式,operator new和placement new介绍 - coderkian

 
阅读更多

new/delete是c++中动态构造对象的表达式 ,一般情况下的new/delete都是指的new/delete表达式,这是一个操作符,和sizeof一样,不能改变其意义。

new/delete表达式的声明如下:

::(optional) new (placement_params)(optional) ( type )initializer(optional)

::(optional) delete expression

除了全局作用符::和初始化参数,还有个 placement_params,这是不常见的,要理解这个参数的作用,就要了解operator new和placement new。

众所周知,new表达式做了两个工作:1.分配内存;2.在分配的内存上调用构造函数构造对象。比如我们分配一个string对象

string *str = new string(“Kian”);

编译器首先调用operator new分配一块内存,类似于malloc,然后在mem上面调用构造函数,

1.void *mem = operator new(sizeof(string));

2. create string at men.

第二步我们是控制不了的,但是operator new却是可以修改的。

Operator new/delete的声明如下:

void* operatornew ( std::size_t count );

void* operatornew ( std::size_t count, const std::nothrow_t& tag);

voidoperator delete ( void* ptr );

voidoperator delete ( void* ptr, const std::nothrow_t& tag);

进入微软、亚马逊,谷歌等美国IT企业工作人才项目,起薪40万,百度搜索(MUMCS)

第二种带参数tag的声明称为nothrow形式,因为现在的operator new如果分配内存失败的话会抛出bad_alloc异常. 有时候我们不想抛出异常,而是根据返回值判断内存分配失败与否,nothrow形式就是这个作用,失败时不抛出异常,而是返回null指针。

我们可以直接重载operator new, 定制自己的内存分配策略,常见的作用是优化内存使用性能。重载operator new不需要看见声明就可以直接使用。我们重定义一个简单的版本:

void* operatornew(std::size_t size){

printf("operator new called, size=%d\n", size);

return malloc(size);

}

voidoperator delete(void *ptr){

printf("operator delete called\n");

free(ptr);

}

int main(){

int *i = newint(2);

delete i;

std::string *str = new std::string("Kian");

delete str;

}

运行结果:

operator new called, size=4

operator delete called

operator new called, size=4

operator new called, size=17

operator delete called

operator delete called

可是有时候我们希望拥有更多的功能,比如记录内存分配释放的位置,用于检测内存错误,或者直接在已有的内存上构造对象,那么必须定义更多参数,这就需要placement new/delete,声明如下.

void* operatornew ( std::size_t count, void* ptr );

void* operatornew ( std::size_t count, user-defined-args... );

voidoperator delete ( void* ptr, void* place);

voidoperator delete ( void* ptr, user-defined-args...);

可以直接在已有内存上构造对象:

void *mem = (void*)malloc(sizeof(int));

int *j = new(mem) int(3);

printf("mem=%p, j=%p, *j = %d\n",mem, j, *j);

free(mem);

运行结果:

mem=0x8a48008, j=0x8a48008, *j = 3

可以看出new直接在mem上面构造了对象。

目前,void* operator new (std::size_tcount, void* ptr )在全局域还不能被重载,但是void* operator new (std::size_tcount, user-defined-args... )可以自由定义。

比如记录内存分配发生的位置:

void* operatornew(std::size_t size){

printf("operator new called, size=%d\n", size);

return malloc(size);

}

voidoperator delete(void *ptr){

printf("operator delete called\n");

free(ptr);

}

void* operatornew(std::size_t size, char* filename, int line){

printf("new called at %s:%d size=%d \n", filename, line, size);

return ::operatornew(size);

}

voidoperator delete(void *place, char* filename, int line) {

printf("delete called at %s:%d place=%p \n", filename, line, place);

::operator delete(place);

}

int main(){

int *k = new(__FILE__, __LINE__) int(1);

printf("k=%p *k=%d \n", k, *k);

delete k;

}

运行结果:

new called at testnew.cpp:41 size=4

operator new called, size=4

k=0x847f008 *k=1

operator delete called

在每个new中打印了文件名和行号,不过细心的你会发现delete时并没有调用重载的placement delete ,这个delete只有在构造对象时抛出了异常才会调用,我们写一个简单的class来看看:

class ThrowExcept {

public:

ThrowExcept(int v):value_(v){ throw1;}

private:

int value_;

};

int main(){

ThrowExcept *t;

try{

t = new(__FILE__, __LINE__) ThrowExcept(10);

}catch(int&e){

printf("catch exception %d\n", e);

}

}

运行结果:

new called at testnew.cpp:53 size=4

operator new called, size=4

delete called at testnew.cpp:53 place=0x9e2e008

operator delete called

catch exception 1

自定义的delete被正常调用,这么做的原因在于如果构造函数抛出异常,系统正常的operator delete并不知道用户自定义的placement new做了什么,自然也不知道怎么去释放。所以如果自己定义placement new, 一定要定义对应的palcement delete,不然可能出现memory leak。

《effective/more effective c++》

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics