对象(探讨对象不适合作为参数和返回值,对象的指针可以的原因)
对象
类的一个实例化。对象中的成员定义从低地址到高地址
1. 对象作为函数参数
对象传参时先将对象中的所有数据进行备份(复制),将复制的数据作为形参传递到调用函数中使用
隐患:拷贝数据中有申请的内存空间,浅拷贝只会拷贝地址不会拷贝数据,在调用函数中会调用一次析构函数释放复制出来的临时对象,在main函数中还会调用一次析构函数释放本来的对象,两次析构,造成内存释放错误
解决方法:
- 使用深拷贝
- 使用引用计数:进入复制构造函数时,记录类对象被复制引用的次数。当对象被销毁时,检查引用计数中保存的引用复制次数是否为0。是,则释放申请的资源,否则引用计数减1
2. 对象指针作为函数参数
传递的数据是指针类型,在函数内的操作都是针对原对象的,不存在对象被复制的问题。因此在函数进入和退出时不会调用构造函数和析构函数,也就不存在资源释放的错误隐患
3. 对象作为返回值
基本数据类型(双精度浮点型以及非标准的__int64类型除外)作为返回值时,通过寄存器eax来保存返回的数据
对象属于自定义类型,寄存器eax无法保存对象中的所有数据
- 在main函数中预留返回对象的栈空间,获取返回对象的栈空间首地址,将申请的返回对象的首地址作为参数压栈,以获得返回对象的数据
- 进入函数后申请返回对象使用的栈空间,退出函数时,将返回对象中数据复制到临时的栈空间中,以这个临时的首地址作为返回值
- 在函数调用结束后进行了数据复制,将函数中的局部对象的数据复制到这个返回对象的空间中,再将这个返回对象复制给目标对象,
注意:此处的返回对象是临时存在的,即临时对象,作用域仅限单条语句
4. 临时对象
调用返回对象的函数时,如 GetCReturn().m_nNumber,此时函数已经退出,其栈帧也被关闭。只能由函数的调用方准备空间,建立临时对象,然后将函数中的局部对象复制给临时对象,再把这个临时对象交给调用方去操作。
当临时对象被销毁时,会执行析构函数,如果析构函数中有对资源释放的处理,可能造成同一个资源多次释放的错误发生
注意:
1)对象作为函数参数,可以传递对象类型的指针
2)当对象作为返回值,如果对象在函数内被定义为局部变量,则不可返回此对象的首地址或引用,以避免返回已经被释放的局部变量