详解new/delete(整合)

C++中内存的动态分配与管理永远是一个让C++开发者头痛的问题,本文通过对C++中内存的动态分配释放的基本原理的介绍,让读者朋友能对C++中的内存的动态分配与释放有较为深入的理解,从而更好驾驭C++程序。

1. 函数(Function)
(1) operator new function

1 void * ::operator new(size_t);                    //Global
2 void * class-name::operator new(size_t);     //Class

上面是C++中operator new function的原型,一个是全局类型的,一个的类成员类型的。全局类型的operator new函数在下面两种情况下被调用:一种是在分配C++内建(built-in)类型的动态内存时,一种是在分配用户没有自己定义operator new成员函数的用户自定义类型的动态内存时。 如果用户在自己定义的类型中,定义了operator new函数,那么用户在用new申请该类型的动态内存时, 便会调用该类型的成员函数operator new, 而不是全局的operator new。

另外,我们注意到,上面的原型中函数的返回值为void *类型, 第一个参数为size_t类型,这个是C++编译器要求的,如果要自己重载operator new函数,返回值必须为void* 类型,第一个参数必须为size_t类型,否则,编译器会返回如下错误信息:

 1 error: ‘operator new’ takes type ‘size_t’ (‘unsigned int’) as first parameter 

这里需要注意的一点是,我们可以利用operator new function可以重载的特点,可以通过参数传入一些额外的信息,来调试程序,检测内存泄露等。比如,我们可以像下面这样重载,传入调用处的行号,函数名,这样就可以跟踪内存的分配使用情况:

1 void * operator new(size_t unSize, int nLine, const char * pFunc)
2 {
3     prinft("Line: %d, Func: %s, allocate %u byte(s)
", nLine, pFunc, unSize);
4     return malloc(unSize);
5 }

(2) operator delete function

1 void operator delete( void * );
2 void operator delete( void *, size_t );

上面是operator delete function的原型。operator delete function也有全局的和类成员的两种。这里需要注意,一个类只能有一个operator delete function做为其成员函数,而且必须为上面两种中的其中一种,没有其它的形式。如果一个类实现了自己的operator delete function成员函数,那么在释放该类型的内存时,编译器便会调用成员operator delete function, 而不是全局的。

上面的两种原型,第一种,在调用的时候,编译器会把要释放的内存的首地址传入,第二种,在调用的时候,编译器会把要释放的内存的首地址和大小都传入。因此,可以利用这一特性,如果我们在基类中实现第二种形式的operator delete function的成员函数,那么便可以用之来释放子类类型的内存(具体参考最后面的例子)。

2. 运算符(Operator)
(1) new operator

[::] new [placement] new-type-name [new-initializer]
[::] new [placement] ( type-name ) [new-initializer]

注:上面的'[]’表示在其中的部分是optional(可选的)
上面是new operator的原型。在C++中,动态内存的分配,通常都是调用new operator来完成的,利用new operator来分配动态内存,编译器要做下面两项工作:

  • a. 调用operator new function分配内存(allocate the memory)
  • b. 调用构造函数(call the constructor)来进行初始化

下面来说一说new operator的原型中各部分到底是干什么的:
placement: 如果你重载了operator new function, placement可以用来传递额外的参数
type-name: 指定要分配的内存的类型,可以是内建(built-in)类型,也可以是用户自定义类型
new-initializer: 指定对分配后内存的初始化的参数,也就的构造函数的参数 。这里需要注意一点,在分配一个对象的数组类型的内存时,不能够指定初始化参数;换言之,要想分配一个对象的数组类型的内存,该对象必须有缺省构造函数

(2) delete opeartor

[::] delete cast-expression
[::] delete [ ] cast-expression

上面是delete operator的原型,第一种用来释放普通的对象(包括内建类型)类型的内存,第二种用来释放对象的数组类型的内存。在C++中,用new operator分配的动态内存,必须调用delete operator来释放,通常用delete operator释放内存编译器要做下面两项工作:

  • a. 调用对象析构函数来析构对象
  • b. 调用operator delete function来释放内存(deallocate the memory)


3. 关于new/delete使用过程中一些需要注意的点
(1)如何区别operator new/delete function 与 new/delete operator ?
通过上面的讲述,不难看出,我们分配/释放动态内存,调用的是new/delete operator, 而在调用new/delete的过程中,编译器会自动调用operator new/delete function来完成实际的内存分配/释放的工作

(2) 用delete operator去释放一块不是由new operator释放的内存,结果是不可预料的,因此,切记,operator new与operator delete一定要配对使用,这是写好程序的基础

(3) new operator调用失败会抛出std::bad_alloc异常,前提是你没有自己重载对应的operator new function;delete operator失败,常见的原因是多次delete同一块内存

(4) 如果一块内存被delete后,再对它解引用(Dereference),结果也是不可预测的,很可能导致程序崩溃

(5) delete一个空(NULL)指针是安全的,没有任何害处的

(6) 类成员类型的operator new/delete函数必须为静态(static)函数,因此它们不能为虚函数(virtual function),也遵守public, protected, private的访问权限控制

4. 关于上面所讲的内容的一些例子:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 
  4 void * operator new(size_t unSize)
  5 {
  6     printf("operator new called
");
  7     return malloc(unSize);
  8 }
  9 void * operator new[](size_t unSize)
 10 {
 11     printf("operator [] called
");
 12     return malloc(unSize);
 13 }
 14 
 15 void * operator new(size_t unSize, int nLine, const char * pFunc)
 16 {
 17     printf("operator new called, line: %d, func: %s
",
 18         nLine, pFunc);
 19     return malloc(unSize);
 20 }
 21 //注意:c++14才支持全局delete或delete【】有多个参数,参看参考资料2.下面两个delete不会覆盖全局的。
 22 void operator delete(void * pMem,size_t unSize)
 23 {
 24      printf("delete1: %u
", unSize);
 25     free(pMem);
 26 }
 27 void operator delete[](void * pMem, size_t unSize)
 28 {
 29           printf("delete[]: %u
", unSize);
 30           free(pMem);
 31  }
 32 
 33 class A
 34 {
 35 public:
 36     
 37     A(int a = 0) :
 38       _a(a)
 39       {   
 40           printf("constructor called
");
 41       }  
 42      virtual ~A()
 43       {
 44           printf("~A()
");
 45       }
 46       void * operator new(size_t unSize)
 47       {
 48           printf(" calledA
");
 49           return malloc(unSize);
 50           
 51       }
 52      //注意:但是支持自定义类型操作符new或delete重载支持size_t参数。即要删除对象的大小delete,要删除对象数组大小delete[].。
 53       void operator delete(void * pMem, size_t unSize)
 54       {
 55           printf("delete2: %u
", unSize);
 56           free(pMem);
 57       }
 58       void operator delete[](void * pMem, size_t unSize)
 59       {
 60           printf("delete[]: %u
", unSize);
 61           free(pMem);
 62       }
 63       
 64 private:
 65     
 66     int _a;
 67 };
 68 
 69 class B: public A
 70 {
 71 public:
 72     //隐式的为静态函数。
 73     void * operator new(size_t unSize, int nLine, const char * pFunc)
 74     {
 75         printf("operator new called, line: %d, fileB: %s
",
 76             nLine, pFunc);
 77         printf("operator new: %u
", unSize);
 78         //_b=0;
 79         return malloc(unSize);
 80     }
 81     
 82     ~B()
 83     {
 84         printf("~B()
");
 85     }
 86     
 87     int _b;
 88     
 89     int _bb;
 90 };
 91 
 92 int main()
 93 {
 94     A * pA = new A(10);
 95     printf("#######
");
 96     
 97     A * pB = new (__LINE__, __FILE__) B();
 98     printf("#######
");
 99     
100     A * szA = new A[10];
101     B *szB = new B[10];
102     printf("#######
");
103     
104     delete pA; 
105     printf("#######
");
106     
107     delete pB; 
108     printf("#######
");
109     
110     delete [] szA;
111     printf("#######
");
112     
113     delete [] szB;
114     printf("#######
");
115 
116 //下面两个不是自定义类,没有类重载new.delete故只能调用全局的,本程序全局不支持size_t参数,故只能调用标准C++中的全局operate delete.故不会打印信息。
117     char * pC = new char[10];
118     delete [] pC; 
119 
120     char *pu = NULL;
121     delete pu;
122 }
clang运行结果如下:

calledA constructor called #######
operator new called, operator new: 16 constructor called ####### operator [] called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called operator [] called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called constructor called ####### ~A() delete2: 8 ####### ~B() ~A() delete2: 16 ####### ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A() delete[]: 84//注意84=4+8*10,数组分配的堆空间,第一个int空间放数组个数,接下来顺序放对象 ####### ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() ~B() ~A() delete[]: 164 ####### operator [] called //调用覆盖的全局函数operate new [],打印信息。 //delete[]调用全局的。

new与delete(new[]与delete[])的调用顺序:类中的重载,本文件中的重载,c++源码的原始操作符函数。

原文地址:https://www.cnblogs.com/loversinJapan/p/4887579.html