1. C++基础—有返回值类型函数(返回引用类型)
1.1 引用的定义
- 引用就是某个目标变量的“别名”(alias),对引用的操作与对变量直接操作效果完全相同。
- 申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不可以把该引用名作为其他变量名的别名。
代码示例:
(1)会调用拷贝构造函数和析构函数 A a(){...;return *this;} (2)不会调用拷贝构造函数和析构函数 A& a(){...;return *this;} 当返回一个变量时,会产生拷贝。当返回一个引用时,不会发生拷贝,你可以将引用看作是一个变量的别名,就是其他的名字,引用和被引用的变量其实是一个东西,只是有了两个名字而已。
注:再次执行相关操作只能是赋值,而不是改改变引用的对象)。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
1.2 引用作为参数传递回顾
- 传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
- 使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
- 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
1.3 将引用作为函数返回值类型
- 格式:类型标识符& 函数名(形参列表及类型说明){ //函数体 }
- 好处:在内存中不产生被返回值的副本;
示例代码一:
#include <iostream> using namespace std; class A { public: A(){} ~A(){} public: void set_x(int x){_x=x;} int& get_x(){return _x;} private: int _x; }; int main() { A *a = new A(); a->set_x(14); int x = a->get_x();//na为新定义的变量,初始化为a->get_a()的值 x = 24; cout<<"x="<<x<<", _x="<<a->get_x()<<endl; int &rx = a->get_x();//na1为引用,是a->get_a()返回变量的别名,初始化为a->get_a()的值 rx = 24; cout<<"rx="<<rx<<", _x="<<a->get_x()<<endl; delete a; system("pause"); return 0; } =>x=24, _x=14 x=24, _x=24
示例代码二:
#include <iostream> #include <string> #include <map> using namespace std; typedef map<int, string> STR_MAP; class A { public: A(){_str_map.clear();} ~A(){} public: STR_MAP& get_str_map() { return _str_map; } void insert_to_str_map(int index, string name) { _str_map.insert(pair<int,string>(index, name)); } void show_str_map() { STR_MAP::iterator iter = _str_map.begin(); for(; iter!=_str_map.end(); ++iter) { cout<<"index="<<iter->first<<", name="<<iter->second<<endl; } } private: STR_MAP _str_map; }; int main() { A *a = new A(); int i=0; for(; i<3; i++) { char cstr[16]; string name(itoa(i, cstr, 16)); a->insert_to_str_map(i, "a" + name); } STR_MAP str_map = a->get_str_map(); str_map.insert(pair<int,string>(i, "b")); a->show_str_map(); cout<<endl; STR_MAP &rstr_map = a->get_str_map(); rstr_map.insert(pair<int,string>(i, "b")); a->show_str_map(); delete a; system("pause"); return 0; } =>index=0, name=a0 index=1, name=a1 index=2, name=a2 index=0, name=a0 index=1, name=a1 index=2, name=a2 index=3, name=b
注:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!
1.4 返回局部变量的引用
返回局部内置类型的引用:
#include <iostream> using namespace std; int& func() { int a; a = 10; return a; } int main() { int &ra = func(); ra += 1; cout<<"a="<<func()<<", ra="<<ra<<endl; ra += 1;//变量a是局部变量,生命周期在执行函数右大括号时结束,其别名也相应结束 cout<<"a="<<func()<<", ra="<<ra<<endl; system("pause"); return 0; } =>a=10, ra=11 a=10, ra=1448013121 程序并未崩溃,只是输出未如预期。
返回局部对象的引用:
#include <iostream> #include <string> using namespace std; string& func() { string a; a = 10; return a; } int main() { string a = func(); cout<<"a="<<a<<endl; string &ra = func(); ra = "1"; cout<<"a="<<func()<<", ra="<<ra<<endl; system("pause"); return 0; } =>编译通过,运行报错 提示:123.exe 中的 0x75644598 处有未经处理的异常: Microsoft C++ 异常: 内存位置 0x00b5ebe8 处的 std::bad_alloc。
注:问题的关键是,当想要返回一个引用而不是一个拷贝时,务必要确保这个引用的有效性。比如:
int& fun() { int a; a=10; return a; }
或 string& fun() { string a; a=”10”; return a; }
这样是不行的,因为a会在fun退出时被销毁,这时返回的a的引用是无效的。这种情况下,如果fun的返回类型不是int&而是int(或string&是string)就没有问题了。
因此,要返回一个引用时,“临时变量”不能是“临时”的,至少得等函数外部使用完毕这个引用之后,才能销毁它。
参考文献:
[1]《C++全方位学习》范磊——第十三章
[2]《C++程序设计教程(第二版)》钱能——第五章、第六章、第七章
[3]《C++ Primer(第5版)》王刚 杨巨峰——第一章、第六章
[4] 百度搜索关键字:C++函数、返回引用类型