有返回值函数

  • 只要函数的返回类型不是 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:控制流可能尚未返回任何值就结束了函数的执行
    //编译器可能检查不出这一错误
}

错误:

  1. for 循环内的 return 语句没有返回值,编译器能检测到这个错误。
  2. 函数在 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"是一个局部变量
}

上面两条语句都将引发未定义的行为:

  1. 对于第一条 return 语句来说,显然它返回的是局部对象的引用。
  2. 在第二条 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;
}

 

posted @ 2019-07-16 17:47  CodeWithMe  阅读(1059)  评论(0)    收藏  举报