黎明的剔透里伸出了你沁清的暧昧

Graphics|EngineDev|GameDev|2D&3D Art

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  60 随笔 :: 1 文章 :: 18 评论 :: 0 Trackbacks
C++中new的3种形态:
new operator 
operator new 
placement new


new operator:
new操作符,像 + - * / && . :: ?: 等操作符一样,是语言内置的, 它不能被重载,不能改变其行为。
它的行为包括分配内存的 operator new 和调用构造函数的 placement new。
new operator 实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针。如果创建的是简单类型(如char)的变量,那么第二步会被省略。
比如:
CTest* pT = new CTest(22);
它的调用实际上等效于:
void*  p  = operator newsizeof(CTest) );//operator new分配内存
CTest* pT = new(p) CTest(22);//placement new调用构造函数,并返回正确的CTest*指针


  
operator new:
原型为: void* operator new(size_t size);
它分配指定大小的内存, 可以被重载, 可以添加额外的参数, 但第一个参数必须为 size_t 。
它除了被 new operator 调用外也可以直接被调用, 如: 
void* p = operator new(sizeof(CTest));

这种用法和调用 malloc 一样, 只分配了sizeof(CTest)大小的内存.



placement new:
置换new,它在一块已经指定的内存上调用构造函数, 包含头文件<new>之后也可以直接使用,
如:
         
CTest* pT = new(p) CTest(22);
它在p这块内存上调用CTest的构造函数来初始化CTest。
这个函数返回一个对象指针,对象是在传递进来的p这块内存上分配的,也就说指定一个额外的自变量(p)作为new operator "隐式调用operator new"时所用,
于是被调用的operator new 除了接受"一定得有的size_t"这个自变量外,还需要接受一个void *的指针,指向一块内存,准备用来接受构造函数好的对象,这样operator new就
是所谓的placement new,其operator new定义如下:

void * operator new(size_t,void *location){
     
return loaction;
}
所以operator new的目的就是为对象找到一块内存,然后返回一个指针指向它.在placement new的情况下,调用者已经知道指向内存的指针,因为调用者知道对象应该放哪,
因此placement new唯一需要做的就是将它获得的指针再返回,至于没有用到(但一定得有的)size_t参数,所以不赋予名称,以免编译器发出"某物未被使用"的警告。
如果用 placement new 构造出来的对象,必须显示的调用对象的析构函数,
如:     
pT->~CTest();
然后释放内存存, 调用 operator delete (对应于分配时的 operator new)operator delete(pT);




 
小结:

1)、想在堆上建立一个对象,应该用 new 操作符,它既分配内存又为对象调用构
        造函数。

2)、如果仅仅想分配内存,就应该调用 operator new 函数;它不会调用构造函数。
    
3)、如果想定制在堆对象被建立时的内存分配过程,应该重载 operator new 函数,
        然后使用 new operator,new operator 会调用定制的 operator new 。

4)、如果想在一块已经获得指针的内存里建立一个对象,应该用 placement new 。 
placement new 主要适用于:
       (a): 对时间要求非常高的应用程序中,因为这些程序分配的时间是确定的;
       (b): 长时间运行而不被打断的程序;
       (c): 以及执行一个垃圾收集器 (garbage collector) 。

注意:如果用 placement new 构造出来的对象,必须显示的调用对象的析构函数。


程序示例:
 1 #include <iostream>
 2 using namespace std;
 3 #include <new>
 4 
 5 
 6 class CTest{
 7 public:
 8     CTest(int _x, int _y){
 9         X = _x;
10         Y = _y;
11     }
12     ~CTest(){
13         X = 0;
14         Y = 0;
15     }
16     void Test(char* sz){
17         cout<<sz<<":"<<"  "<<"X="<<X<<"   "<<"Y="<<Y<<endl;
18     }
19     int X;
20     int Y;
21 };
22 
23 //new operator:
24 void F1(){
25     CTest* pT = new CTest(11);             // new operator
26     
27     pT->Test("F1");
28 
29     delete pT;
30 
31 }
32 
33 
34 //    operator new
35 //    placement new
36 
37 void F2(){
38     void*  p  = operator new(sizeof(CTest)); // operator new : 分配内存
39     CTest* pT = new(p) CTest(22);          // placement new: 构造对象
40     
41     pT->Test("F2");
42     
43     pT->~CTest();                            // 必须显示析构对象
44     operator delete(pT);                     // operator delete: 释放内存
45 }
46 
47 
48  //   也可这样实现:
49 void F3(){
50     char*  p  = new char[sizeof(CTest)]; // new operator: char为内置类型,不会调用构造函数,相当于只分配内存
51 
52     CTest* pT = new(p) CTest(33);      // placement new: 在这块内存上构造CTest对象
53     
54     pT->Test("F3");
55     
56     pT->~CTest();                        // 必须显示析构CTest对象
57 
58     delete [] p;                         // delete operator: char为内置类型,不会调用析构函数,相当于只释放内存
59 }
60 
61 
62 void main(){
63     F1();
64     F2();
65     F3();
66 } 
F1:  X=1   Y=1
F2:  X=2   Y=2
F3:  X=3   Y=3
Press any key to continue


 
C++中的delete:
为了避免resource leaks,每一个动态分配行为都应该必须匹配一个相应但相反的释放动作;
如果你写:
string *ps;
delete ps;//使用delete operator;相对与 new operator
指向delete ps;要求编译器既能够析构ps所指的对象,又能够释放被该对象占用的内存,
其中释放内存是由operator delete所执行的(相对于 operator new),其声明如下:

void operator delete(void *memoryTOBeDeallocated);

因此执行delete ps; 那么编译器就产生了如下代码:
ps->~string();               //调用对象的析构函数dtoroperator
operator delete(ps);       //释放对象所占的内存

这里呈现的一个暗示就是:若果你打算处理原始的、未设置初值的内存,应该完全回避new operator和delete operator ,改调用operator new 取得内存,operator delete归还系统内存
void *buffer=operator new(50*sizeif(char)); // 分配内存相当于C语言中的malloc

operator delete(buffer);       //释放内存 但没用调用任何析构函数 相当于C语言中的free

如果你使用了placement new在某块内存上产生对象,你应该避免对那块内存使用delete operator,这是因为delete operator会调用operator delete来释放内存,但是该内存内含的对象最初并非是由operator new分配的,毕竟placement new只返回它所接受的指针而已,但不知道这个指针是从哪里来的,所以为了抵消该对象的构造函数的影响,你应该直接调用该对象的析构函数

class Widget{
public:
    Widget(
int WidgetSize);
    
};

Widget
* constructWidgetInBUuffer(void *buffer,int WidgetSize){
        
return new(buffer) Widget(WidgetSize);
}
//以下函数用来分配及释放shared memory中的内存
void *mallocShared(size_t size);
void freeShared(void *memory);

void *sharedMemory=mallocShared(sizeof(Widget));

Widget 
*pw=constructWidgetInBUuffer(sharedMemory,10);//运用placement new

delete pw;
//无定义!!,因为sharedMemory来自mallocShared,不是来自operator new

pw
->~Widget();//OK!!析构pw所指的Widget对象,但未释放Widget对象的内存

freeShared(pw);
//OK!!  释放pw所指的内存 不用调用任何析构函数








posted on 2011-10-30 00:22 情绝格调(fresmaster) 阅读(411) 评论(0)  编辑 收藏 引用 所属分类: Coding

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理