目录
- 一、对new的理解
- 1、new做了什么
- 2、new被编译器转为了什么
- 3、operate_new源代码长啥样
- 二、对delete的理解
- 1、delete做了什么
- 2、delete被编译器转为了什么
- 3、operator delete源代码长啥样
- 三、构造函数与析构函数的直接调用
- 参考
一、对new的理解
1、new做了什么
C++告诉我们,new的时候会分配一块内存用来放对象,分配好之后会调用构造函数。所以说所谓的自动调用,其实是被new调用的。
所以总结new做的动作:1、分配内存 2、调用构造函数
2、new被编译器转为了什么
以下面语句为例:
Complex* pc = new Complex(1,2);
仿造编译器的流程,可以具化为:
try{//1、allocatevoid* 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=1cout << 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
参考
https://www.bilibili.com/video/BV1Kb411B7N8?p=7