条款20:以const-reference传递替换by-value传递
2015-05-29 09:48
477 查看
缺省情况下,C++中函数参数的传递方式为by-value。即函数都是以实际参数的副本进行传递,而函数返回的也是一个副本。考虑如下实例程序:
现在分析一下上述函数执行流程:执行validateStudent(s)传入参数是先调用一次copy构造函数构造一个s的副本,从该函数退出时,再调用一次析构函数销毁s的副本。此外,Student中有一个string变量,需要调用一次string的构造函数,Student继承自Person,因此需要调用一次Person构造函数,而Person中又有两个string,再调用两次string的构造函数,因此总共需要构造5次,与之对应的需要析构5次,这就是by-value传递的代价。
那么我们如何才能不构造就进行参数传递呢?当然是const-reference了,如下:
Bool validateStudent(const Student& s);
这种参数传递方式不涉及任何的构造与析构调用。
同时通过by-value方式传递参数也可以造成对象被截断(slicing)的问题,如下所示:
怎么会出现这种情况呢?display()可是虚函数啊,它不应该执行多态调用吗?原来是参数传递出现问题了。值传递中,无论传入的是什么类型,其构造副本的时候只是按照形参的类型来构造,也就是说传入的副本是个Window类型的,这种现象被称为截断。
如果改为以引用传递会如何呢?
我们必须知道引用的本质就是用指针实现的。因此传入到是当前对象本身而不是副本,因此会发生多态调用了。
注意:
我们如上讨论的主要问题就是by-value传递会执行很多的构造与析构过程,而by-reference传递会很好地解决这个问题。但是并不是所有类型的变量都适合by-reference传递。比如内置类型、STL迭代器、函数对象,对它们而言,by-value传递往往比较合适,并且效率高些。
#include <iostream> class Person { public: Person(){ cout << "Person的构造函数" << endl; } virtual ~Person(){ cout << "Person的析构函数" << endl; } Person(const Person& p){ cout << "Person的copy构造函数" << endl; } private: string name; string address; }; class Student : public Person { public: Student(){ cout << "Student的构造函数" << endl; } ~Student(){ cout << "Student的析构函数" << endl; } Student(const Student& p){ cout << "Student的copy构造函数" << endl; } void setID(string id){ studentID = id; } string getID() const{ return studentID; } private: string studentID; }; bool validateStudent(Student s) { return s.getID().length() != 0 ? true : false; } int main() { Student s; s.setID("123456"); bool isOK = validateStudent(s); std::cout << "validateStudent(): " << isOK << std::endl; }
现在分析一下上述函数执行流程:执行validateStudent(s)传入参数是先调用一次copy构造函数构造一个s的副本,从该函数退出时,再调用一次析构函数销毁s的副本。此外,Student中有一个string变量,需要调用一次string的构造函数,Student继承自Person,因此需要调用一次Person构造函数,而Person中又有两个string,再调用两次string的构造函数,因此总共需要构造5次,与之对应的需要析构5次,这就是by-value传递的代价。
那么我们如何才能不构造就进行参数传递呢?当然是const-reference了,如下:
Bool validateStudent(const Student& s);
这种参数传递方式不涉及任何的构造与析构调用。
同时通过by-value方式传递参数也可以造成对象被截断(slicing)的问题,如下所示:
#include <iostream> using namespace std; class Window { public: string name() const{ return "Window"; }; // 返回窗口名 virtual void display(){ cout << "Display Window" << endl; }; // 显示窗口 }; class EXWindow : public Window { public: virtual void display(){ cout << "Display EXWindow" << endl; }; }; void printNameAndDisplay(Window w) { cout << "窗口名:" << w.name() << endl; w.display(); } int main() { EXWindow exw; 27 printNameAndDisplay(exw); return 0; }
怎么会出现这种情况呢?display()可是虚函数啊,它不应该执行多态调用吗?原来是参数传递出现问题了。值传递中,无论传入的是什么类型,其构造副本的时候只是按照形参的类型来构造,也就是说传入的副本是个Window类型的,这种现象被称为截断。
如果改为以引用传递会如何呢?
void printNameAndDisplay(const Window& w) { cout << "窗口名:" << w.name() << endl; w.display(); }
我们必须知道引用的本质就是用指针实现的。因此传入到是当前对象本身而不是副本,因此会发生多态调用了。
注意:
我们如上讨论的主要问题就是by-value传递会执行很多的构造与析构过程,而by-reference传递会很好地解决这个问题。但是并不是所有类型的变量都适合by-reference传递。比如内置类型、STL迭代器、函数对象,对它们而言,by-value传递往往比较合适,并且效率高些。
相关文章推荐
- EasyUI-panel 内嵌页面上的js无法被执行
- 修改class默认编译目录build
- 检测到有潜在危险的 Request.Form 值
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
- Dui界面布局无法响应鼠标点击消息响应
- Hadoop报错 " Message missing required fields: callId, status"解决方案
- UIAlertView及UIActionSheet 在ios8极其以下版本的兼容问题解决方案
- EasyUI Dialog with iFrame
- 学习笔记:UITabBarController使用详解
- JPA注解-@SequenceGenerator
- Mysql缓存Query Cache原理
- 关于EasyUIDataGrid查询功能
- 用c#开发微信 (8) 微渠道 - 推广渠道管理系统 3 UI设计及后台处理
- media query(媒体查询)和media type(媒体类型)
- Troubleshooting Guide ORA-3136: WARNING Inbound Connection Timed Out (文档 ID 465043.1)
- 前端框架 EasyUI (2)页面布局 Layout
- why constrained regression and Regularized regression equivalent
- 前端框架 EasyUI (1)熟悉一下EasyUI
- Problem A Number Sequence(KMP基础)
- ant 的实用例子