Chinaunix首页 | 论坛 | 博客
  • 博客访问: 299032
  • 博文数量: 148
  • 博客积分: 4365
  • 博客等级: 上校
  • 技术积分: 1566
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-05 21:38
文章分类
文章存档

2014年(2)

2013年(45)

2012年(18)

2011年(1)

2009年(54)

2008年(28)

我的朋友

分类: C/C++

2013-01-19 00:38:41

Item 49: Understand the behavior of the new-handler

先参考:  ,回顾下new。PS:这网站看名字就知道不错。

最常见的new,像 int *p = new int; 这种称为 new-expression,它的原型两种:

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


可以结合C++0x中的auto关键字来使用:auto p = new auto('c');  

上面的p类型就是char *类型,如果使用了auto,那么initializer就不是optional了,必须要写。(之前这里写成不能写了)


使用 new 创建多维数组时,第一维可以是一个值为正整数的数学表达式,也只有第一维可以。

直接分配在栈区的数组是不可以的。


int n = 42;
double a[n][5]; // Error
auto p1 = new double[n][5]; // OK
auto p2 = new double[5][n]; // Error

如果表达式的值计算是负的,或者太大无法分配,c++0x会抛出 std::bad_array_new_length 这个异常。


new和delete可以重载定制自己的版本,编译器会在当前空间首先寻找,即一个类成员函数使用new时,会先在类内空间寻找是否有定制版本,一层层寻找。

直接调用默认版本可以使用全局域作用符, ::new。


initializer如果没有写,则会调用默认的构造函数创建对象。

class base
{
public:
    base(int f=4):i(f){cout<<"in cons i:"<
输出如下,第一个不带初始化参数,第二个带


in cons i:4
in cons i:6

第二个是operator new,在标准头文件里面,不过不导入也能用,它默认隐含在每一个编译单元(文件)。它的作用是只分配内存,不调用对象构造函数进行初始化。


void* operator new  ( std::size_t count );
void* operator new[]( std::size_t count );
void* operator new  ( std::size_t count, const std::nothrow_t& );
void* operator new[]( std::size_t count, const std::nothrow_t& );
void* operator new  ( std::size_t, void* ptr );
void* operator new[]( std::size_t, void* ptr );
带[]是用于分配数组的,例如 new int[5],实际内部调用的就是operator new[],当然这种会有一些其他额外(overhead)开销,比如额外分配空间记住分配了多少个元素啊什么的。
int main()
{
    base * p = (base *)operator new (sizeof(base));
    delete p;
    return 0;
}

这个程序看不到base构造函数中的输出,其实看看它的参数,size_t类型就可以明白,还有其返回值是 void*,注意转换。

第一行版本如果分配内存失败,会调用一个用户注册的回调函数进行处理,就是本item标题中提到的:

  typedef void (*new_handler)();
  new_handler set_new_handler(new_handler) throw();

这些都在里面,c++0x中还多一个new_handler get_new_handler(),不过gcc4.6里面还没看到。

回调函数完成后,会抛出std::bad_alloc异常。

第三行版本是老式c风格的版本,和第一个版本差异是不抛出异常,返回值为NULL,第二个参数可以写nothrow,里面的一个全局变量。

  extern const nothrow_t nothrow;

忘了在哪看的了,const变量定义如果没有extern,则其它文件不能引用的。

第五行版本直接返回第二个入参,说是给placement_new用的,没明白,那要第一个参数干吗。


placement_new就是new的第三种形式,它不用于分配内存,只是在已分配的内存上创建对象。

申请内存涉及到OS,对程序而言开销是很大的,当两个程序共享内存时就可以直接创建对象了,这只是一种场景。

语法形式是:

new (place_address) type
new (place_address) type (initializer-list)

int main()
{
	base * p = (base *)operator new (sizeof(base));
	new( p) base(2) ;
	p->~base();
	delete p;
	cout<<"end in main\\n";
	return 0;
}
in cons i:2

end in main

要注意的是由于这里是手动创建对象,也就需要手动调用该对象的析构函数,释放其占据的资源,最后归还内存。

这里要注意下:调用的是默认的delete,其本身就会触发调用地址p上对象的析构函数,即如果~base()函数中有打印的话,会看到两次,

一次是手动调用的,一次是delete触发的。如果调用的是operator delete,则只会看到一次:

operator delete(p);


scott在这个item强调的是最好不要使用那个不抛出异常的new,虽然看着判NULL和c风格很兼容。申请对象内存空间成功时,不代表对象一定能创建成功,比如其构造函数内又申请内存等等。只要一个地方有抛异常的new,就有可能出现异常并扩散,没有捕获代码的话将是杯具。

不过google编程规范里不用异常,那也是他们最初就没有用,使用的话改动太多吧,难道都是判NULL的。

阅读(1338) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~