返回值类型


在定义函数时,必须定义返回值类型,如果函数不返回值,使用void表示不返回任何数值;若定义了返回值类型不为void,函数最后要使用return返回数值,否则编译器失败。

返回值与函数定义的返回值类型之间的行为,类似=指定时表达式与变量之间的关系,因此也可以定义返回值类型为指针、lvalue 参考、rvalue 参考等。

如果返回地址,那么返回值类型可定义为指针类型,这代表着内存地址在函数执行完毕后,必须仍是有效的,也就是说这通常代表着,函数内动态配置内存,例如:

#include <iostream> 
using namespace std; 

int* makeArray(int m, int initial = 0) { 
    int *a = new int[m]; 
    for(int i = 0; i < m; i++) {
        a[i] = initial; 
    }
    return a; 
}

void deleteArray(int* arr) {
    delete [] arr; 
}

int main() { 
    int m = 0; 

    cout << "数组大小: "; 
    cin >> m; 

    int *arr = makeArray(m); 

    for(int i = 0; i < m; i++) {
        cout << "arr[" << i << "] = " << arr[i] << endl; 
    }

    deleteArray(arr);

    return 0; 
}

执行结果:

数组大小: 5
arr[0] = 0
arr[1] = 0
arr[2] = 0
arr[3] = 0
arr[4] = 0

如果不是使用new来动态配置,在函数中定义的变量内存,在函数执行结束后会自动消毁,返回地址就没有意义,编译器会提出警讯,执行时期往往也会造成访问错误。

也可以定义返回值类型为 lvalue 参考或 rvalue 参考,然而类似地,不该将局部变量以 lvalue 参考返回,或者将常量以 rvalue 参考返回,因为函数执行完毕后,局部变量、常量的内存就会被回收了。

在定义函数时,定义返回类型为 lvalue 参考通常是为了效率,例如,以下的longerStr,在传递实参或返回值时,都会发生string内容的复制:

#include <iostream> 
using namespace std; 

string longerStr(string s1, string s2) {
    return s1.length() > s2.length() ? s1 : s2;
}

int main() { 
    string s1 = "Justin Lin";
    string s2 = "Monica Huang";
    string s3 = longerStr(s1, s2);
    cout << s3 << endl;

    return 0; 
}

这只是单纯比较string的长度,返回较长的实例,复制内容若是多余的,可以改为以下:

#include <iostream> 
using namespace std; 

string& longerStr(string &s1, string &s2) {
    return s1.length() > s2.length() ? s1 : s2;
}

int main() { 
    string s1 = "Justin Lin";
    string s2 = "Monica Huang";
    string &s3 = longerStr(s1, s2);
    cout << s3 << endl;

    return 0; 
}

C++ 14 开始,在前后文可以推断类型的情况下,返回值类型可以使用auto,例如:

#include <iostream> 
using namespace std; 

auto& longerStr(const string &s1, const string &s2) {
    return s1.length() > s2.length() ? s1 : s2;
}

int main() { 
    string s1 = "Justin Lin";
    string s2 = "Monica Huang";
    auto &s3 = longerStr(s1, s2);
    cout << s3 << endl;

    return 0; 
}

在上例中,C++ 11 必须定义返回类型为const string&,然而 C++ 14 可以使用auto&;参数不能使用auto自动推断类型,因为没有推断的来源信息。

类似地,定义返回值类型为 rvalue 参考,通常也是为了效率。例如:

#include <iostream> 
using namespace std; 

string&& concat(string &&lhs, string &rhs) {
    lhs += rhs;
    return std::move(lhs);
}

int main() { 
    string s = "++";
    string result = concat("C", s);
    cout << result << endl;

    return 0; 
}

在这个例子中,实参"C"是个常量,参数lhs接管了该常量,因为函数执行完之后,lhs生命周期也就结束,不会再使用,使用std::movelhs当成是 rvalue 返回,因此lhs的内容将移动至result,而不是复制至result,如果最后返回的lhs是个很长的字符串,效率上会比较好。


展开阅读全文