C++ 传值 避免 内存泄漏的一个技巧。[new 了以后,不一定要delete][修正,new后一定delete,没人帮你释放的。我错了,此文章已没任何意义了]
不用看了,2年前,自己功夫不够,没有想清楚,也觉得奇怪呢,忘记了 拷贝构造函数 这一说,结果导致本文 没有意义了。
其实我根本没有使用过这个想法的,后来的工作依然是 一个 new 一个delete。
原来以为 C++ 会自动进行类型转换,我错了。
再次声明,以下是错误的,现在经过修正了。红色为修正后。
----------------------
直接上代码。其实这是对类 和 指针 理解的一个案例。
#include<stdio.h> #include<iostream> using std::endl; using std::cout; /***************** 事实证明: 类的一般实例对象 只在所在函数有效,函数结束,这个类实例自动释放资源。 ***************/ #define DEBUG_FUNCTION_LINE() printf("#当前所在函数[%p] %s() 位于%d行\n",this,__FUNCTION__,__LINE__) class CServer1{ public: int isExit; public: CServer1(){ this->isExit=0; DEBUG_FUNCTION_LINE(); };
CServer1(const CServer1©){
DEBUG_FUNCTION_LINE();
this->isExit=copy.isExit;
};
~CServer1(){ this->isExit=1; DEBUG_FUNCTION_LINE(); }; }; ////下面这个 函数 是否有缺陷??? CServer1 getPtrFunc (){ CServer1 *s=new CServer1(); s->isExit=15; return *s;//如果不用指针,局部变量会有错误的。Error 返回局部变量地址 是错误的。
//这里错误了,函数返回,发生了默认拷贝构造函数,函数内部的 new 没有释放掉,倒是新派生出了一个返回值 的类。那个 可以在本函数外围 自动释放掉。
}; void func2(){ CServer1 foo; foo=getPtrFunc();//这样赋值,是否 会存在内存泄漏???当然不会 实际存在内存泄漏了。 std::cout<<" foo->isExist=" << foo.isExit <<endl;//=15 是正常的 //结束后,会执行 释放 操作的! }; void func3(){ getPtrFunc();//这样赋值,是否 会存在内存泄漏???当然不会当然会 new了就要delete的 //结束后,会执行 释放 操作的! }; int main(){ func2();//两次 析构函数 其实是三次构造,两次析构,错误的。 cout<<"@@@ Func2 完毕。"<<endl; func3();//一次 析构函数 //这个函数证明了 很是OK !太错误了,看结果就知道 其实是 两次构造函数,一次析构函数 cout<<"@@@ Func3 完毕。"<<endl;return 0; }
结果:
#当前所在函数 CServer1::CServer1() 位于18行
#当前所在函数 CServer1::CServer1() 位于18行
#当前所在函数 CServer1::~CServer1() 位于22行
foo->isExist=15
#当前所在函数 CServer1::~CServer1() 位于22行
@@@ Func2 完毕。
#当前所在函数 CServer1::CServer1() 位于18行
#当前所在函数 CServer1::~CServer1() 位于22行
@@@ Func3 完毕。
#当前所在函数[0x7fff5a39ec08] CServer1() 位于17行
#当前所在函数[0x7fbb60c03a00] CServer1() 位于17行
#当前所在函数[0x7fff5a39ec00] CServer1() 位于20行
#当前所在函数[0x7fff5a39ec00] ~CServer1() 位于29行
foo->isExist=15
#当前所在函数[0x7fff5a39ec08] ~CServer1() 位于29行
@@@ Func2 完毕。
#当前所在函数[0x7fbb60c03a10] CServer1() 位于17行
#当前所在函数[0x7fff5a39ec18] CServer1() 位于20行
#当前所在函数[0x7fff5a39ec18] ~CServer1() 位于29行
@@@ Func3 完毕。
构造函数 和 析构函数 是一一对应的。所以看到上面 没有少任何一个函数 ,说明 没有内存泄漏。
我用了一个 new ,但是没有delete。没有内存泄漏。
new的类,利用 函数返回值(以前我一直认为返回值 如果是类,必须得用指针呢,其实不用),直接返回类的实例对象,而不是指针,这样,new出来的就不需要delete,也会自然调用 析构函数了。
哈哈,,我好像 是 一个 小学生 突然发现 吸铁石 S N 互相吸引 ,SS NN互相排斥 一样。。。[果然跟小学生似的]
突然又想 反着来,返回一个指针 类型的,必须 delete 才能避免内存泄漏。但是 发现 使用函数返回值 我错了一次。。。
#include<stdio.h> #include<windows.h> #include<iostream> using std::endl; using std::cout; /***************** 事实证明: 类的一般实例对象 只在所在函数有效,函数结束,这个类实例自动释放资源。 ***************/ #define DEBUG_FUNCTION_LINE() printf("#当前所在函数[%p] %s() 位于%d行\n",this,__FUNCTION__,__LINE__) class CServer1{ public: int isExit; public: CServer1(){ this->isExit=0; DEBUG_FUNCTION_LINE(); };
CServer1(const CServer1©){
DEBUG_FUNCTION_LINE();
this->isExit=copy.isExit;
}; ~CServer1(){ this->isExit=-1; DEBUG_FUNCTION_LINE(); }; }; ////下面这个 函数 是否有缺陷??? CServer1 getPtrFunc (){ CServer1 *s=new CServer1(); s->isExit=15; return *s;//如果不用指针,局部变量会有错误的。Error 返回局部变量地址 是错误的。 //这句话 会 调用一个 析构函数 }; void func1(){ CServer1 foo; cout<<"开始 调用getPtrFunc返回值"<<endl; foo=getPtrFunc();//这样赋值,是否 会存在内存泄漏???当然不会 cout<<"结束 调用getPtrFunc返回值"<<endl; std::cout<<" foo->isExist=" << foo.isExit << " [=15 是正常的]" <<endl;// //结束后,会执行 [拷贝后的对象]释放 操作的! }; void func2(){ cout<<"开始 调用getPtrFunc返回值"<<endl; getPtrFunc();//这样赋值,是否 会存在内存泄漏???当然不会 cout<<"结束 调用getPtrFunc返回值"<<endl; //结束后,会执行 释放 操作的! }; CServer1 func31(){ CServer1 * s=new CServer1(); s->isExit=25; *s=getPtrFunc(); std::cout<<"In Func31: foo->isExist=" << s->isExit << " [=15 是正常的]" <<endl;//=15 是正常的 return *s;//本句 导致new的对象 析构函数 被调用了。。。特无语啊。 //return CServer1(); }; CServer1 * func3(){ //此函数 第一个版本 //CServer1 *s; //s=&func31();//这一句 彻底失败了。 应该是赋值 而不是 引用地址!! //cout<<"@@@ Func3.1完毕。"<<endl; //Sleep(1); //std::cout<<"In Func3: foo->isExist=" << s->isExit <<endl;//=15 是正常的 //此函数 第二个版本 正常,但是不是我想要的 /***** CServer1 s;//更改为 s 才正常了。 cout<<"开始 调用Func31。"<<endl; s=func31(); cout<<"结束 调用Func31。"<<endl; Sleep(1); std::cout<<"In Func3: foo->isExist=" << s.isExit << " [=15 是正常的]" <<endl;//=15 是正常的 return NULL; *********/ //此函数 第三个版本,修改了返回值类型;此函数 如果不delete 会内存泄漏 CServer1 *ptr; ptr=new CServer1(); cout<<"开始 调用Func31。"<<endl; *ptr=func31(); cout<<"结束 调用Func31。"<<endl; Sleep(1); std::cout<<"In Func3: foo->isExist=" << (*ptr).isExit << " [=15 是正常的]" <<endl;//=15 是正常的 return ptr; }; int main(){ func1();//两次 析构函数 cout<<"@@@ Func1 完毕。"<<endl<<endl; func2();//一次 析构函数 //这个函数证明了 很是OK ! cout<<"@@@ Func2 完毕。"<<endl<<endl; CServer1 * p=func3(); cout<<"@@@ Func3 完毕。"<<endl<<endl; delete p; cout<<"最后一个函数完成了,delete下 也就没有内存泄漏了!!"<<endl; Sleep(20000); return 0; } /* #当前所在函数 CServer1::CServer1() 位于18行 开始 调用getPtrFunc返回值 #当前所在函数 CServer1::CServer1() 位于18行 #当前所在函数 CServer1::~CServer1() 位于22行 结束 调用getPtrFunc返回值 foo->isExist=15 [=15 是正常的] #当前所在函数 CServer1::~CServer1() 位于22行 @@@ Func1 完毕。 开始 调用getPtrFunc返回值 #当前所在函数 CServer1::CServer1() 位于18行 #当前所在函数 CServer1::~CServer1() 位于22行 结束 调用getPtrFunc返回值 @@@ Func2 完毕。 #当前所在函数 CServer1::CServer1() 位于18行 开始 调用Func31。 #当前所在函数 CServer1::CServer1() 位于18行 #当前所在函数 CServer1::CServer1() 位于18行 #当前所在函数 CServer1::~CServer1() 位于22行 In Func31: foo->isExist=15 [=15 是正常的] #当前所在函数 CServer1::~CServer1() 位于22行 结束 调用Func31。 In Func3: foo->isExist=15 [=15 是正常的] @@@ Func3 完毕。 #当前所在函数 CServer1::~CServer1() 位于22行 最后一个函数完成了,delete下 也就没有内存泄漏了!!
#当前所在函数[0x7fff57113b48] CServer1() 位于18行
开始 调用getPtrFunc返回值
#当前所在函数[0x7f90e0403a00] CServer1() 位于18行
#当前所在函数[0x7fff57113b38] ~CServer1() 位于22行
结束 调用getPtrFunc返回值
foo->isExist=15 [=15 是正常的]
#当前所在函数[0x7fff57113b48] ~CServer1() 位于22行
@@@ Func1 完毕。
开始 调用getPtrFunc返回值
#当前所在函数[0x7f90e0403a10] CServer1() 位于18行
#当前所在函数[0x7fff57113b58] ~CServer1() 位于22行
结束 调用getPtrFunc返回值
@@@ Func2 完毕。
#当前所在函数[0x7f90e0403a20] CServer1() 位于18行
开始 调用Func31。
#当前所在函数[0x7f90e0403a30] CServer1() 位于18行
#当前所在函数[0x7f90e0403a40] CServer1() 位于18行
#当前所在函数[0x7fff57113ab8] ~CServer1() 位于22行
In Func31: foo->isExist=15 [=15 是正常的]
#当前所在函数[0x7fff57113b38] ~CServer1() 位于22行
结束 调用Func31。
In Func3: foo->isExist=15 [=15 是正常的]
@@@ Func3 完毕。
#当前所在函数[0x7f90e0403a20] ~CServer1() 位于22行
最后一个函数完成了,delete下 也就没有内存泄漏了!!
*/
总结:
1.在getPtrFunc 函数中,虽然只有new,由于返回值 是 类实例 类型的,将指针 *p 返回,会在函数完成后自动销毁(即调用了和new对应delete 对应的 析构函数)。
2. 返回值类型为类实例的函数 的时候,不能对函数 使用 & 。正如 func3 的第一个版本,其实已经访问了释放过内存的区域了。(isExist=-1了)是不安全的。
3.上面说明,函数返回值 其实在取值的时候 是赋值,而不是 传递 地址。将 内存 拷贝,复制,有点 memcpy 的感觉。char[] 数组 如果可以这样赋值 多方便。
------------------------------------------------------------------------------------------------
一定要专业!本博客定位于 ,C语言,C++语言,Java语言,Android开发和少量的Web开发,之前是做Web开发的,其实就是ASP维护,发现EasyASP这个好框架,对前端后端数据库 都很感觉亲切啊。. linux,总之后台开发多一点。以后也愿意学习 cocos2d-x 游戏客户端的开发。