/ new-->申请空间+调用构造函数?先申请空间再调用构造函数
// delete-->释放空间+调用析构函数?先调用析构函数再去释放空间
// 有没有问题?--什么问题?
void FunTest1()
{
Test* p1 = (Test*)malloc(sizeof(Test));
Test* p2 = new Test;
// free(p1);
// delete p2;
delete p1;
free(p2);
}
非内部数据类型:编译器自身不认识需要用户自己定义的而讲
对于非内部数据类型光用malloc和free是无法满足动态对象的,因为对象在创建的时候需要编译器自动调用构造函数,而malloc是一个库函数不是运算符,不在编译器的控制范围之内自然不能把调用构造函数的任务强加给malloc,而new可以看成是两步:1>动态分配内存相当于malloc
2>调用类的构造函数
new建立的是一个对象
malloc是分配的一块内存。
free和delete也是同理:free只是单纯的释放分配的内存,并不能调用析构函数完成清理工作。
// 析构函数如果显式给出程序崩溃, 为什么?
void FunTest2()
{
Test* p1 = new Test;
Test* p2 = new Test[10];
delete[] p1;
delete p2;
}
Test* p1 = new Test;
创建了单个对象只调用了一次构造函数
Test* p2 = new Test[10];
创建了10个类对象调用了次构造函数
如果我们显示定义了析构函数,new Test[1]他仍然会比实际用户需要的字节数多出4个字节,因此在释放的时候他会先将cout取出来,然后再去调用count次析构函数。最后再释放内存。
delete[] p1;
它本来是要释放之前创建单个对象的内存的,并且之前只调用了一次构造函数也应该只析构一次,但是此时却多次调用了析构函数将本来不属于这个对象的空间当成了该类型的对象进行清理程序自然会崩溃,而如果我们将析构函数不显示给出,那么他相当于没有调用析构函数自然不会去清理不属于该对象的空间程序也不会崩溃。如果去我们显式的给出析构函数,那我们知道它会先去取出前4个字节中保存的cout也就是创建对象的个数,然后去析构count次那么你就会把不属于你的空间也清理了,因此程序会崩溃。如果我们不显式的给出析构函数那么它就不会多出那4个字节
delete p2;
它本来是要将之前创建的10个对象的内存释放掉的,之前调用了10次构造函数,那么此时它也应该调用10次析构函数去完成清理工作而此时的delete相当于只清理了第一个对象,并没有完成清理完所有对象自然会出问题。
---------------------------------------------------------------------上述都是对类对象即非内置类型来讲的。
而对于内置类型的没有构造和析构函数,因此在释放内存的时候delete和delete[]的效果是一样的,不存在匹配问题。
无论new还是new[],C++必须知道返回指针所指向的内存块的大小,否则它就不可能正确地释放掉这块内存,但是在用new[]为一个数组申请内存时,编译器还会悄悄地在内存中保存一个整数,用来表示数组中元素的个数(这是编译器动得手脚)。因为在delete一块内存时,我们不仅要知道指针指向多大的内存,更重要的是要知道指针指向的数组中对象的个数。因为只有知道了对象数量才能一一调用它们的析构函数,完成对数组中所有对象的清理。如果使用的是delete,则编译器只会将指针所指的对象当作单个对象来处理。所以对于数组,需要使用delete[]来处理;符号[]会告诉编译器在delete这块内存时,先去获取保存的那个元素数量值,然后再进行一一清理
对于new和delete的深层认识:
new:
我们都知道new[cout]在申请分配的内存时候底层是调用了operator new[]函数---[]里面代表所要申请的字节数如果我们显式定义了析构函数那么他申请的字节数要多加4个字节,
然后调用了operator new函数(参数也是字节数同刚才一样
然后调用了malloc函数去为我们真正的申请内存空间
如果显式定义了析构函数那么我们将cout放入用户数据起始地址的位置也就是将cout放入前4个字节中。
接着我们调用cout次构造函数。
由此我们可以知道析构函数显式给出new出来的字节数会多出来4个去存放cout。
delete:
在使用delete这个操作符的时候如果之前我们显式定义了析构函数那么我们首先将cout取出来然后调用cout次的析构函数,然会在调用operator delete[]函数--接着调用operator delete函数---然后在调用free函数去释放。
当然我们的operator new函数可以自己写成类的成员函数
也可以写成普通函数
也可以直接调用系统自己写函数
当然这三个函数会有先后顺序,如果三个同时给出则会先调用我们写成的类成员函数,然后调用普通函数最后才是系统自己的函数,当然在使用new时如果调用自己写的
operator new函数之后也会去调用构造函数,在开辟完空间之后就会去自动调用构造函数去创建对象。
定位new表达式不用开辟空间只调用它的构造函数
我们可以用定位new来模拟实现new
class Test
{
public :
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test *New(size_t size)
{
Test*p = (Test*)malloc(size);
if (p == NULL)
{
return NULL;
}
new(p)Test;
return p;
}
private:int _Data;
};
int main()
{
Test p;
Test *pt=p.New(44);
system("pause");
return 0;
}
当然上面是直接创建单个对象
我们也可以用定位new来实现new[]
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test *New(size_t count)//new出count个对象
{
int *p = (int *)malloc(count*sizeof(Test)+4);
if (p == NULL)
{
return NULL;
}
*p = count;
Test *pt = (Test *)(p + 1);
for (int idx = 0; idx < count; idx++)
{
new (pt + idx)Test();
}
return pt;
}
private:int _Data;
};
int main()
{
Test p;
cout << "please look" << endl;
Test *pt = p.New(4);
system("pause");
return 0;
}
我们可以看到用New()创建了4个对象调用了4次构造函数。