有返回值函数
- 只要函数的返回类型不是 void ,则该函数的每条 return 语句必须返回一个值。
- return 语句返回值的类型必须与函数返回类型相同,或者能隐式地转换成函数的其他类型的表达式将产生编译错误。
尽管C++无法确保结果的正确性,但是可以保证每个 return 语句的结果类型正确。编译器尽量确保具有返回值的函数只能通过一条有效的 return 语句退出:
//因为含有不正确的返回值,所以这段代码无法通过编译 bool str_subrange(const string& str1, const string& str2) { //大小相同:此时普通的相等性判断结果作为返回值 if (str1.size() == str2.size()) return str1 == str2;//正确:==运算符返回布尔值 //得到较短string对象的大小 auto size = (str1.size() < str2.size()) ? str1.size() : str2.size(); //检查两个string对象的对应字符串是否相等,以较短的字符串长度为限 for (decltype(size)i = 0; i != size; ++i) { if (str1[i] != str2[i]) return;//错误 #1:没有返回值,编译器将报告这一错误 } //错误 #2:控制流可能尚未返回任何值就结束了函数的执行 //编译器可能检查不出这一错误 }
错误:
- for 循环内的 return 语句没有返回值,编译器能检测到这个错误。
- 函数在 for 循环之后没有提供 return 语句,编译器也许能检测到这个错误,也许不能,如果编译器没有发现这个错误,则运行时的行为将是未定义的。
值是如何被返回的
返回一个值的方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。
必须注意当函数返回局部变量时的初始化规则:
//如果ctr的值大于1,返回word的复数形式 string make_plural(size_t ctr, const string& word, const string& ending) { return (ctr > 1) ? word + ending : word; }
该函数返回类型是 string,意味着返回值将被拷贝到调用点。函数将返回 word 的副本或者一个未命名的临时 string 对象,该对象的内容是 word 和 ending 的和。
如果函数返回引用,则该引用仅是它所引用对象的一个别名:
//挑出两个string对象中较短的那个,返回其引用 const string& shorterString(const string& s1, const string& s2) { return s1.size() <= s2.size() ? s1 : s2; }
形参和返回类型都是 const string 的引用,不管是调用函数还是返回结果都不会真正拷贝 string 对象。
不要返回局部对象的引用或指针
函数完成后,它所占用的存储空间也随之释放掉。函数终止也就意味着局部变量的引用将指向不再有效的内存区域:
//严重错误:这个函数试图返回局部对象的引用 const string& manip() { string ret; //以某种方式改变一下ret if (!ret.empty()) return ret;//错误:返回局部对象的引用! else return "Empty";//错误:"Empty"是一个局部变量 }
上面两条语句都将引发未定义的行为:
- 对于第一条 return 语句来说,显然它返回的是局部对象的引用。
- 在第二条 return 语句中,字符串字面值转换成一个局部临时 string 对象,该对象和 ret 一样都是局部的。
当函数结束时临时对象占用的空间也就随之释放掉了,所以两条 return 语句都指向了不可再用的内存空间。
返回类类型的函数和调用运算符
调用运算符的优先级与点运算符和箭头运算符相同,并且也符合左结合律。
如果函数返回指针、引用或类的对象,就能使用函数调用的结果访问结果对象的成员。通过如下形式得到较短 string 对象的长度:
//调用string对象的size成员,该string对象是由shorterString函数返回的 auto sz = shorterString(s1, s2).size();
因为上面提到的运算符都满足左结合律,所以 shorterString 的结果是点运算符的左侧运算对象,点运算符可以得到该 string 对象的 size 成员,size 又是第二个调用运算符的左侧运算对象。
引用返回左值
函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。可以像使用其他左值那样来使用返回引用的函数的调用。
能为返回类型是非常量引用的函数的结果赋值:
char& get_val(string& str, string::size_type ix) { return str[ix]; } int main() { string s("a value"); cout << s << endl;//输出 a value get_val(s, 0) = 'A';//将s[0]的值改为A cout << s << endl;//输出A value return 0; }
返回类型是常量引用,不能给调用的结果赋值:
shorterString("hi", "bye") = "x";//错误:返回值是个常量
列表初始化返回值
C++11 新标准规定,函数可以返回花括号包围的值的列表。此处的列表页用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化,否则,返回的值由函数的返回类型决定。
vector<string> process() { string expected, actual; if (expected.empty()) return {};//返回一个空vector对象 else if (expected == actual) return { "functionX","okay" };//返回列表初始化的对象 else return { "functionX",expected,actual }; }
- 如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。
- 如果函数返回的是类类型,由类本身定义初始值如何使用。
主函数 main 的返回值
如果函数的返回类型不是 void ,那么它必须返回一个值。
例外:允许 main 函数没有 return 语句直接结束。如果控制到达了 main 函数的结尾处而且没有 return 语句,编辑器将隐式地插入一条返回0的 return 语句。
main 函数的返回值可以看做是状态指示器。返回0表示执行成功,返回其他值表示执行失败,其中非0值的具体含义依机器而定。
int main() { bool some_failure; if (some_failure) return EXIT_FAILURE;//定义在cstdlib头文件中 else return EXIT_SUCCESS;//定义在cstdlib头文件中 }
递归
//计算val的阶乘,即1 * 2 * 3...* val int factorial(int val) { if (val > 1) return factorial(val - 1) * val; return 1; }

浙公网安备 33010602011771号