new delete new[]和delete[]的面试总结和底层实现的介绍

1.new delete总结

分配释放类别可否重载
malloc()free()C函数不可
newdeleteC++表达式不可
::operator new()::operator delete()C++函数
allocator<T>::allocate()allocator<T>::deallocate()C++ 标准库可自由设计并搭配任何容器

new就是new operator,new[]就是 array new,new()就是placement new,::operator new()就是operator new函数,这是一个函数,下面一一详细介绍一下。

2.new expression(new operator)

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

上述的new我们称为new表达式,也就是关键字new,有的书上称为new operator,Complex是自己定义的一个类一个复数罢了,new的动作为先申请一块内存,然后调用构造函数。源代码抽象如下:

Complex *pc;
try{
    void* mem = operator new(sizeof(Complex));//allocate
    pc = static_cast<Complex*>(mem);//cast 指针类型的转型
    pc->Complex::Complex(1,2);//construct 注意只有编译器可以这样调用ctor(构造函数)
}
catch(std::bad_alloc){
    //若allocation失败就不执行constructor
}

可以看到我们外部并不能直接通过指针去直接调用构造函数,但可以通过placement new来调用构造函数,形式为new(p)Complex(1,2),p为一个Comlex指针。

3.operator new函数

void *operator new(size_t size, const std::nothrow_t&)
                    _THROW0()
{
void *p;
while((p==malloc(size))==0){
        _TRY_BEGIN
            if(_callnewh(size)==0) break;
        _CATCH(std::bad_alloc) return(0);
        _CATCH_END
    }
return (p);
}

以上代码是operator new 函数的源代码,该函数时可以被重载的,该函数输入一个size,malloc申请一个size的空间并返回,如果申请失败,会调用你可以自己定义的一个_callnewh(size)的一个函数,可以自己编辑怎么样删除多余的其他内存的函数,然后好分配给你正在需要申请的内存。std::nothrow_t表示这个函数不会抛出异常

4.delete expression

delete做的事情,先调用析构函数,然后把内存释放掉,调用接口如下:

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

编译器编译后如下:

pc->~Complex();//先析构
operator delete(pc); //然后释放内存

前面说我们不能直接通过指针来调用构造函数,但是可以直接调用析构函数,operator delete的源码如下,可看出是调用的free函数。

void __cdecl operator delete(void *p) _THROW0(){
    free(p);
}

5.array new(new[]), array delete(delete[])

很容易理解array new就是一次申请多个对象,并调用多次构造函数,而之前的new只调用了一次构造函数,array delete就是先进行多次析构函数,之前的delete只调用了一次析构函数,在delete[]进行多次析构函数时,有的是倒着析构的,即最后一个对象开始析构,有的是正着析构的。如果该调用delete[]的地方调用了delete,一般对于有指针的对象是有影响的,一般对于没有指针的类是没有影响的。

下面进行一个测试对比来看看这个过程。

#include<iostream>
using namespace std;
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; }
};
int main() {
	int size = 3;
	A* buf = new A[size]; //必须要有默认构造函数
	A* tmp = buf;
	cout << "buf=" << buf << " tmp=" << tmp << endl;

	for (int i = 0; i < size; i++)
		new(tmp++)A(i);  //placement new,通过指针直接调用构造函数
	cout << "buf=" << buf << " tmp=" << tmp << endl;
	delete[] buf;
	while (1);
}

输出结果如下:

在上述代码中,调用delete[]和调用delete的效果是一样的,因为析构函数的作用其实不大,不会发生内存泄漏,下面是使用delete的实验结果,你用VS跑可能会报异常,不用管,如果你用内置类型就不会出现这样的问题了。

下面我们进行一个新的测试那就是在类的里面定义指针,看delete[]和delete的结果有何区别:

class A {
public:
	string* id;
    int capacity;
	A(int cap = 5)  { 
        capacity = cap;
		id = new string[capacity];
	}
	~A() { 
		delete id;
	}
};

int main(){
    A* p = new A[3];
    delete[] A;
}

这里使用的是delete会导致报错,因为string不是内置类型,在delete的时候析构函数是有意义的,而只有delete id的话,也就只调用了一次析构函数,因此是错误的。在调用delete的时候如果要释放数组,不是内置类型的需要使用delete[],内置类型用delete和delete[]都可以,因为在malloc的时候记录了总共要删除多大的内存,自定义类有指针就不能直接使用delete释放数组,没有指针一般用delete或者delete[]都可以。因为如果有指针申请了一块新的内存那么析构函数就很重要,申请多少个对象就要执行多少次析构函数。对比如下:

int* p = new int[10];
delete p;//和delete[] p相同的

string* p = new string[10];
delete[] p;//不可以使用delete p因为string 不是内置类型,构造函数有意义,必须执行构造函数

那么会有同学问我自己写了一个类,里面成员都是内置类型那么我可以delete和delete[]随便用吗,答案是可以的,一般只要成员没有指针都没有关系。

6.placement new

placement new允许我们将对象构造于已经分配好的内存中,没有placement delete,因为placement new 根本没有分配内存。placement new的使用方式如下,可看到是先分配了一块内存,然后调用指针在已经分配的内存上构造了一个Complex对象。

#include<new>
char *buf = new char[sizeof(Complex)*3];
Complex *pc = new(buf)Complex(1,2);
....
delete[] buf;

下面我们看抽象后的placement new源码:

Complex *pc;
try{
    void* mem = operator new(sizeof(Complex),buf);
    pc = static_cast<Complex*>(mem);
    pc->Complex::Complex(1,2);
}
catch(std::bad_alloc){
}

该operator new 函数的源码如下:

void* operator new(size_t, void* loc){
    return loc;//因为不需要分配内存,因此只需要把这个定点的指针返回即可
}

7.参考资料

C++内存管理机制-从平地到万丈高楼(完结)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

USTC暖暖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值