词条 | 返回值 |
释义 | 一个函数的函数名既是该函数的代表,也是一个变量。由于函数名变量通常用来把函数的处理结果数据带回给调用函数,即递归调用,所以一般把函数名变量称为返回值。 C++中的返回值 函数可以有返回值,也可以没有返回值。对于没有返回值的函数,功能只是完成一个操作,应将返回值类型定义为void,函数体内可以没有return语句,刚需要在程序指定位置退出时,可以在该处放置一个 例: void backward(int n) { cout<<n%10; if(n<10) return; else backward(n/10); } int main() { int n; cout<<"输入整数:"<<endl; cin>>n; cout<<"原整数:"<<n<<endl<<"反向数:"; backward(n); cout<<endl; return 0; } 函数可以有返回值,只要 return 就可以给出一个。不过我们常常不用它--有两个原因: 1、C/C++里返回值是复制出去的,而对于大的对象,复制的代价很高; 2、有些对象是不能复制的--至少编译器不知道怎么复制--比如数组。 于是我们有了很多这样的函数: bool GetObj(ObjType& obj); bool Encode(const char* src, char* dest); 用一个参数来代替返回值,而返回值只是指示函数执行是否成功。我本人一直固执的认为,这是C的处理方式,C++不该这样,返回就是返回,就该光明正大的返回,而不是在文档里为某个参数悄悄的注上: // out 诚然返回一个大对象是困难的,但这个困难是 C 程序员的,而不是 C++ 程序员的 -- 我们可以返回指针。C也有指针,但很少有人敢在 C 函数里返回一个指针,因为: 1、如果指针指向栈变量,毫无疑问,要么你不用这个返回值,要么是一个错误; 2、如果指针指向堆变量,要么你在祈祷用这个函数的程序员会好好的看文档且足够细心会调用 free,要么就是内存泄漏; 3、如果指针指向 static 变量,那么用这个函数的程序员牢牢记住“下次调用这个函数以后,上次的返回值也会跟着变”,要么就是你被别人骂成“专出 BUG 的垃圾”。 在C++里直接返回裸指针的话,情况并不会有什么起色,不过 C++ 有智能指针的,通常它们指向堆变量,占用的空间和裸指针一样大。考虑前面第一个函数,我们写成: std::auto_ptr<ObjType> GetObj(); 那么一切迎刃而解,返回值如果你不要,作为临时变量,会立即被析构,返回的对象被释放;如果你要,就得赋值给另一个智能指针。总之不用程序员记得,编译器会保证这个对象的释放。 考虑第二个函数,稍微有一点麻烦,因为 auto_ptr 是不能用来持有数组的,不过,在C++的世界里,std::string 几乎总是比 char* 好用: std::auto_ptr<std::string> Encode(const char* src); 最后考虑最麻烦的情况: bool AssembleObjList(ObjType objList[], size_t length); 这种类型的函数无论是在 C 标准库里,还是在各种操作系统的 API 里,比比皆是,事实上它存在两大缺陷: 1、如果需要的数量超出给出的,要么是一个安全问题(经典的缓冲区溢出,如 strcpy),要么是失败,程序员不得不作出估计--众所周知,程序员的估计能力比他们的薪水低得多; 2、如果执行成功,到底 Assemble 了多少个? 于是我们见到了这样的函数: bool AssembleObjList(ObjType objLIst[], size_t* lengthPtr); 这个函数通常是两步调用的: size_t length = 0; AssembleObjList(0, &length); ObjType* objList = new ObjType[length]; AssembleObjList(objList, &length); for(size_t i=0; i<length; ++i)// 处理每个元素 这种形式能解决上面列出的两个问题,但这实在是太麻烦、太可恶了。我的答案仍然是--C++程序员应该用C++的库: std::auto_ptr<std::vector<ObjType> > AssembleObjList(); 又干净,又舒服! C++标准库里居然没有一个可以持有数组的智能指针,所以 boost库不错,可以这样: std::pair<boost::scoped_array<ObjType>, size_t> AssembleObjList(); 可惜这个并不比标准库的解法更优秀--因为要返回数目的缘故,不过我个人更喜欢这个解,因为他更接近最优的解--传说中的 trule 手法: TruleVector<ObjType> AssembleObjList(); 其中 TruleVector是这样一个模板:它只有两种操作,一是构造,二是自动转型为 std::vector,而且它具有 “auto_ptr 式的所有权转移语义”。也就是说,TruleVector除了作为数组型的返回值,你无法把他用于其它任何任务。 不过由于 std::vector具有值语义,所以写的代码还是稍微有一点不同寻常: typedef std::vector<ObjType> ObjListType; ObjListType list; list.swap(AssembleObjList()); 最后一句在有的产品上可能要写成: list.swap(static_cast<ObjListType>(AssembleObjList())); 这样别扭的原因,可以归咎于C++库里缺一个引用语义的线性容器。 |
随便看 |
百科全书收录4421916条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。