C++ 内存基本构件new/delete的意义、运用方式以及重载方式

407 阅读3分钟

目录

一、对new的理解

1、new做了什么

C++告诉我们,new的时候会分配一块内存用来放对象,分配好之后会调用构造函数。所以说所谓的自动调用,其实是被new调用的。
所以总结new做的动作:1、分配内存 2、调用构造函数

2、new被编译器转为了什么

以下面语句为例:

Complex* pc = new Complex(1,2);

仿造编译器的流程,可以具化为:

try{
	//1、allocate
	void* mem = operate_new(sizeof(Complex));
	//2、cast,将指针转型为Complex*类型指针
	pc = static_cast<Complex*>(mem);
	//3、construct,注意这种写法只有编译器才能使用,我们需要避免这种行为
	pc->Complex::Complex(1,2);
}
catch(std::bad_alloc){
	//若allocation失败就不执行构造函数
}

注意第3点,如果想要直接调用ctor,可以使用placement new:

new(p)Complex(1,2);

3、operate_new源代码长啥样

这里截取的是vc98版本的源代码:
在这里插入图片描述
可见,如果分配内存成功,就返回分配到的内存的指针,否则陷入while循环中。
什么时候会失败?大量耗用内存之后,我们需要new一个对象,会发现我们调用不到内存了。
这时会使用callnewh函数,即调用newhandle函数,这是一种自设定的函数。也就是说,分配内存失败就会调用你设定的那个函数。我们需要在newhandle函数中释放内存,以便调用完newhandle函数后会有内存给malloc分配。
关于函数的第二个参数
nothrow与异常的抛出有关,它是不抛异常,意思是说operate_new这个函数是
保证不抛异常的
,在新版的C++特性中,不抛异常的写法有做修改。
它的解释如下:
struct std::nothrow_t {};

The struct is used as a function parameter to operator new to indicate that the function should return a null pointer to report an
allocation failure, rather than throw an exception.

二、对delete的理解

1、delete做了什么

C++告诉我们,delete的时候会先调用析构函数,然后调用delete函数释放内存。

2、delete被编译器转为了什么

先调用delete函数:

Complex* pc = new Complex(1,2);
...
delete pc;

被编译器转为:

pc->~Complex();			//先析构,注意这里可以直接调用析构函数
operator delete(pc);	//然后释放内存

3、operator delete源代码长啥样

也就是直接调用free函数。
在这里插入图片描述
总结一下,new与delete调用的是operate_new和operator delete。而operate_new调用的是malloc函数,operator delete调用的是free函数。

三、构造函数与析构函数的直接调用

先通过指针调用构造函数,这里先选择string类,因为string在标准库里面是个typedefine,本名为basic_string。编译器把第一个string换成了basic_string,后面再找string没有找到,所以这里会报错。这个并不是真正不能使用构造函数的原因。
例1:

 string* pstr = new string;
    cout << "str= " << *pstr << endl;
    
//! pstr->string::string("jjhou");  
                        //[Error] 'class std::basic_string<char>' has no member named 'string'
//! pstr->~string();	//crash -- 其語法語意都是正確的, crash 只因為上一行被 remark 起來嘛.  
    cout << "str= " << *pstr << endl;

例2:
可以看到在GCC里面通过指针或者直接调用构造函数是不正确的,在VC中,条件会放宽。

class A
{
public:
  int id;
  
  A() : id(0)      { cout << "default ctor. this="  << this << " id=" << id << endl;  }
  A(int i) : id(i) { cout << "ctor. this="  << this << " id=" << id << endl;  }
  ~A()             { cout << "dtor. this="  << this << " id=" << id << endl;  }
};

A* pA = new A(1);         	//ctor. this=000307A8 id=1
  	cout << pA->id << endl;   	//1
//!	pA->A::A(3);                //in VC6 : ctor. this=000307A8 id=3
  								//in GCC : [Error] cannot call constructor 'jj02::A::A' directly
  								
//!	A::A(5);	  				//in VC6 : ctor. this=0013FF60 id=5
                      			//         dtor. this=0013FF60  	
  								//in GCC : [Error] cannot call constructor 'jj02::A::A' directly
  								//         [Note] for a function-style cast, remove the redundant '::A'
		
  	cout << pA->id << endl;   	//in VC6 : 3
  								//in GCC : 1  	
  	
  	delete pA;                	//dtor. this=000307A8 

参考

www.bilibili.com/video/BV1Kb…