C++中new 的用法大全 详细.docx
《C++中new 的用法大全 详细.docx》由会员分享,可在线阅读,更多相关《C++中new 的用法大全 详细.docx(14页珍藏版)》请在三一办公上搜索。
1、C+中new 的用法大全 详细深入C+的new new是C+的一个关键字,同时也是操作符。关于new的话题非常多,因为它确实比较复杂,也非常神秘,下面我将把我了解到的与new有关的内容做一个总结。 new的过程 当我们使用关键字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针。当然,如果我们创建的是简单类型的变量,那么第二步会被省略。假如我们定义了如下一个类A: class A int i; public: A(int _i) :i(_i*_i) void Say printf(i=%dn, i); ; /调用new: A* pa = new
2、A(3); 那么上述动态创建一个对象的过程大致相当于以下三句话: A* pa = (A*)malloc(sizeof(A); pa-A:A(3); return pa; 虽然从效果上看,这三句话也得到了一个有效的指向堆上的A对象的指针pa,但区别在于,当malloc失败时,它不会调用分配内存失败处理程序new_handler,而使用new的话会的。因此我们还是要尽可能的使用new,除非有一些特殊的需求。 new的三种形态 到目前为止,本文所提到的new都是指的new operator或称为new expression,但事实上在C+中一提到new,至少可能代表以下三种含义:new operat
3、or、operator new、placement new。 new operator就是我们平时所使用的new,其行为就是前面所说的三个步骤,我们不能更改它。但具体到某一步骤中的行为,如果它不满足我们的具体要求时,我们是有可能更改它的。三个步骤中最后一步只是简单的做一个指针的类型转换,没什么可说的,并且在编译出的代码中也并不需要这种转换,只是人为的认识罢了。但前两步就有些内容了。 new operator的第一步分配内存实际上是通过调用operator new来完成的,这里的new实际上是像加减乘除一样的操作符,因此也是可以重载的。operator new默认情况下首先调用分配内存的代码,尝
4、试得到一段堆上的空间,如果成功就返回,如果失败,则转而去调用一个new_hander,然后继续重复前面过程。如果我们对这个过程不满意,就可以重载operator new,来设置我们希望的行为。例如: class A public: void* operator new(size_t size) printf(operator new calledn); return :operator new(size); ; A* a = new A; 这里通过:operator new调用了原有的全局的new,实现了在分配内存之前输出一句话。全局的operator new也是可以重载的,但这样一来就不能再
5、递归的使用new来分配内存,而只能使用malloc了: void* operator new(size_t size) printf(global newn); return malloc(size); 相应的,delete也有delete operator和operator delete之分,后者也是可以重载的。并且,如果重载了operator new,就应该也相应的重载operator delete,这是良好的编程习惯。 new的第三种形态placement new是用来实现定位构造的,因此可以实现new operator三步操作中的第二步,也就是在取得了一块可以容纳指定类型对象的内存后,
6、在这块内存上构造一个对象,这有点类似于前面代码中的p-A:A(3);这句话,但这并不是一个标准的写法,正确的写法是使用placement new: #include void main char ssizeof(A); A* p = (A*)s; new(p) A(3); /p-A:A(3); p-Say; 对头文件或的引用是必须的,这样才可以使用placement new。这里new(p) A(3)这种奇怪的写法便是placement new了,它实现了在指定内存地址上用指定类型的构造函数来构造一个对象的功能,后面A(3)就是对构造函数的显式调用。这里不难发现,这块指定的地址既可以是栈,又可
7、以是堆,placement对此不加区分。但是,除非特别必要,不要直接使用placement new ,这毕竟不是用来构造对象的正式写法,只不过是new operator的一个步骤而已。使用new operator地编译器会自动生成对placement new的调用的代码,因此也会相应的生成使用delete时调用析构函数的代码。如果是像上面那样在栈上使用了placement new,则必须手工调用析构函数,这也是显式调用析构函数的唯一情况: p-A; 当我们觉得默认的new operator对内存的管理不能满足我们的需要,而希望自己手工的管理内存时,placement new就有用了。STL中的
8、allocator就使用了这种方式,借助placement new来实现更灵活有效的内存管理。 处理内存分配异常 正如前面所说,operator new的默认行为是请求分配内存,如果成功则返回此内存地址,如果失败则调用一个new_handler,然后再重复此过程。于是,想要从operator new的执行过程中返回,则必然需要满足下列条件之一: l 分配内存成功 l new_handler中抛出bad_alloc异常 l new_handler中调用exit或类似的函数,使程序结束 于是,我们可以假设默认情况下operator new的行为是这样的: void* operator new(si
9、ze_t size) void* p = null while(!(p = malloc(size) if(null = new_handler) throw bad_alloc; try new_handler; catch(bad_alloc e) throw e; catch() return p; 在默认情况下,new_handler的行为是抛出一个bad_alloc异常,因此上述循环只会执行一次。但如果我们不希望使用默认行为,可以自定义一个new_handler,并使用std:set_new_handler函数使其生效。在自定义的new_handler中,我们可以抛出异常,可以结束程
10、序,也可以运行一些代码使得有可能有内存被空闲出来,从而下一次分配时也许会成功,也可以通过set_new_handler来安装另一个可能更有效的new_handler。例如: void MyNewHandler printf(New handler called!n); throw std:bad_alloc; std:set_new_handler(MyNewHandler); 这里new_handler程序在抛出异常之前会输出一句话。应该注意,在new_handler的代码里应该注意避免再嵌套有对new的调用,因为如果这里调用new再失败的话,可能会再导致对new_handler的调用,从而
11、导致无限递归调用。这是我猜的,并没有尝试过。 在编程时我们应该注意到对new的调用是有可能有异常被抛出的,因此在new的代码周围应该注意保持其事务性,即不能因为调用new失败抛出异常来导致不正确的程序逻辑或数据结构的出现。例如: class SomeClass static int count; SomeClass public: static SomeClass* GetNewInstance count+; return new SomeClass; ; 静态变量count用于记录此类型生成的实例的个数,在上述代码中,如果因new分配内存失败而抛出异常,那么其实例个数并没有增加,但coun
12、t变量的值却已经多了一个,从而数据结构被破坏。正确的写法是: static SomeClass* GetNewInstance SomeClass* p = new SomeClass; count+; return p; 这样一来,如果new失败则直接抛出异常,count的值不会增加。类似的,在处理线程同步时,也要注意类似的问题: void SomeFunc lock(someMutex); /加一个锁 delete p; p = new SomeClass; unlock(someMutex); 此时,如果new失败,unlock将不会被执行,于是不仅造成了一个指向不正确地址的指针p的存在
13、,还将导致someMutex永远不会被解锁。这种情况是要注意避免的。 STL的内存分配与traits技巧 在STL原码剖析一书中详细分析了SGI STL的内存分配器的行为。与直接使用new operator不同的是,SGI STL并不依赖C+默认的内存分配方式,而是使用一套自行实现的方案。首先SGI STL将可用内存整块的分配,使之成为当前进程可用的内存,当程序中确实需要分配内存时,先从这些已请求好的大内存块中尝试取得内存,如果失败的话再尝试整块的分配大内存。这种做法有效的避免了大量内存碎片的出现,提高了内存管理效率。 为了实现这种方式,STL使用了placement new,通过在自己管理的
14、内存空间上使用placement new来构造对象,以达到原有new operator所具有的功能。 template inline void construct(T1* p, const T2& value) new(p) T1(value); 此函数接收一个已构造的对象,通过拷贝构造的方式在给定的内存地址p上构造一个新对象,代码中后半截T1(value)便是placement new语法中调用构造函数的写法,如果传入的对象value正是所要求的类型T1,那么这里就相当于调用拷贝构造函数。类似的,因使用了placement new,编译器不会自动产生调用析构函数的代码,需要手工的实现: te
15、mplate inline void destory(T* pointer) pointer-T; 与此同时,STL中还有一个接收两个迭代器的destory版本,可将某容器上指定范围内的对象全部销毁。典型的实现方式就是通过一个循环来对此范围内的对象逐一调用析构函数。如果所传入的对象是非简单类型,这样做是必要的,但如果传入的是简单类型,或者根本没有必要调用析构函数的自定义类型,那么再逐一调用析构函数是没有必要的,也浪费了时间。为此,STL使用了一种称为type traits的技巧,在编译器就判断出所传入的类型是否需要调用析构函数: template inline void destory(For
16、wardIterator first, ForwardIterator last) _destory(first, last, value_type(first); 其中value_type用于取出迭代器所指向的对象的类型信息,于是: template inline void _destory(ForwardIterator first, ForwardIterator last, T*) typedef typename _type_traits:has_trivial_destructor trivial_destructor; _destory_aux(first, last, triv
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+中new 的用法大全 详细 C+ new 用法 大全

链接地址:https://www.31ppt.com/p-3153658.html