1.解析new delete
new 底层调动malloc开辟object大小(自动计算)的空间 然后通过定位new调用构造函数,创建对象并初始化10,把构建好的对象的地址给op,op指向这个空间。
堆区中的这个对象是没有名字的
op->Print(); (*op).Print(); 这两种方式一样
delete op; 先调用析构函数析构对象,然后释放内存(资源)
此时是空悬指针 最好在delete op ;后加上 op=nullptr;
开辟10个对象,要调用10次构造函数,主函数结束,调用10次析构函数
重点来了
系统怎么知道op是指向的是一个对象还是一组对象?
new Object[n] 开辟n个对象,系统知道连续开辟了n个对象,开辟10个Object空间,实际上new还多开辟了4个字节的空间,在上面多开辟4个空间,先记录开辟空间的个数(10记录在案),delete[]op删除的时候,它告诉其删除的是一组对象(10个),[]告诉其必须要上移4个字节来读取这里面的值(10),调用10次析构函数。
如果用delete op; 删除连续的多个空间,会导致错误。此语句只删除1个对象,而且在开辟空间的时候,有上越界标记和下越界标记,还有一个头部信息(给堆区看的),new申请空间的大小存在头部信息,创建对象个数在自己创建的空间。delete op;向上读,会产生内存读的错位,导致这里的删除让程序崩溃。
真正的op指向创建的第一个对象的地址
malloc申请空间(是用户空间)后,协议:在用户空间之上,我们给了4个字节的上越界标记,和4个字节的下越界标记,当我们访问到越界标记时,认为访问空间越界,malloc还在上面开辟28个字节,其中4个字节用于记录你申请的字节个数,free释放时可以向上读取这个值,知道当时申请的空间个数。new构建对象delete析构对象。
[]先上移4个字节来读取这里面的值(10),调用10次析构函数释放对象。然后调动free上移空间读取头部信息,知道malloc当时申请的空间,释放空间。
如果是delete op;去释放数组开辟的空间,系统认为你将释放一个对象,指向开始,读取头部信息,错了4个字节,所以读取头部信息不完整,
如果只是申请单个对象:Object *op=new Object(10);
先调用malloc申请空间(用户空间),然后有上越界标记和下越界标记,最上面是头部信息申请空间的个数。释放的时候,delete op;知道只是释放一个对象,向上读取头部信息,知道空间大小,先释放对象然后释放空间,
如果是创建一个对象,用delete[]op;,系统认为指向的这个位置点上4个字节是创建空间(对象)的个数,可是这是上越界标记,导致系统误认,导致程序崩溃,越界标记的值是FDFDFDFD(被误认为对象的个数)
2.引用的第二次讲解
底层 编译器编译的时候,把引用当做指针来处理
常引用 注意二义性
如果本身是变量,可以拿普通引用或者常引用
如果本身是常量,只能常引用
函数的返回类型是引用类型
方框是eax
引用返回的时候:本质上是地址,寄存器放的是b的地址,x=(b);把所指之物b的值给x ,这种程序实际上是错误的:当从主函数执行,调动funb,定义b,引用返回,临时空间装的是b的地址,实际上,return之后,funb的空间已经被释放了,回到主函数的时候,我们通过这个地址要抓它的值,但是这个地址的空间已经被释放了,所以我们是从失效指针抓数据了,这是不允许的,这种程序是错误的,虽然在单线程中似乎可以,但是在多线程必然有问题。局部变量生存期受到函数的影响。
注意 int &funb() return b,在系统底层看来,返回的是b的地址,int x=funb(); 系统其实是执行(&b),把b的值10直接赋值给了x。如果是int &x=funb(); 在系统底层,&x相当于是指针x,return b;子函数相当于把b的地址给了x指针,x指针接收b的地址,而子函数执行完,子函数的空间就被释放了,如果后面还有其他函数的调用,就会打扰到这个空间,这时候输出的x的值就会是随机值或者改变的值。
右值引用
右值:不可以取地址,1.常量是右值。
int a=10; 这个10就是右值,不能取地址。系统也称重右值。
int &&c=10;右值引用,可以去引用10
int&&x=a;a不能引用,因为a是左值,不能拿右值引用
2.将亡值
左值:可以取地址
上图这个编译不允许通过
如果再加上一个& ,编译就通过了
上图代码,可以把将亡值延长寿命,寿命和x等同。
右值引用将亡值
系统怎么做?
当调动主函数的时候,给主函数分配一个栈空间,当调动funa的时候,给了a=10,return的时候,产生一个副本,主函数右值引用接收,把a的副本创建到主函数的栈帧空间中,x的引用就是引用这个将亡值.将亡值和x生存期一样了。
下面这个程序对不对?
程序虽然运行成功,但是是错误的!!!
先看下面这个例子
运行后,结果是随机值!!!
主函数调用,给主函数分配一个栈,调用funa函数,给其分配一个栈,定义的变量为10;return以引用的形式返回a,实际上是返回a的地址,回到主函数的时候,x是引用,实际上也是一个指针,把a的地址给x,如果不调用fun,我们去打印x,funa原本在的这个空间如果不去骚扰(已经被释放),可以打印10,因为没有覆盖,如果又执行了fun,又把这个栈帧分配出来了,进行了进一步初始化,赋值,打印的要么改了要么随机值
引用在类中的应用
引用返回
此函数:可以通过它取值和赋值(对函数的返回值:当前对象的引用)
常对象不让修改值
因为上图这个方法是普通方法,有可能 在这个普通方法对value做手脚
看下图
当系统编译是,函数里面要添加Object * const this
当Object对象调用它时,把地址给this指针,对于常方法来说,它也有this指针
给函数加const,实际上const是修饰指针的指向不可改变。this可以指向value,但是通过this 不可以改变value的值,常引用返回。
虽然函数名相同,但是参数类型不一样,可以重载‘
’
拷贝构造函数
给c1分配空间,再给c2分配空间,然后调动构造函数,在c1空间,依次构建c1对象,然后到CGgood c1(c1);缺省的拷贝构造函数,把c1,c2的地址抓住,一个一个赋值进去,按位拷贝
拷贝构造函数
st前必须加&,否则将形成递归建立,无限循环
看下图这个字符串例子
程序运行,崩溃了!
叫做浅拷贝
当对象使用完,不结束,调用s2的析构函数,把指针释放了,置为空,然后调用s1的析构函数,又一次释放空间,同一个空间释放了2次