缺省情况下,C++中函数参数的传递方式为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() != ? true : false;
} int main()
{
Student s;
s.setID("");
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 ;
}


怎么会出现这种情况呢?display()可是虚函数啊,它不应该执行多态调用吗?原来是参数传递出现问题了。值传递中,无论传入的是什么类型,其构造副本的时候只是按照形参的类型来构造,也就是说传入的副本是个Window类型的,这种现象被称为截断。

如果改为以引用传递会如何呢?

 void printNameAndDisplay(const Window& w)
{
cout << "窗口名:" << w.name() << endl;
w.display();
}


我们必须知道引用的本质就是用指针实现的。因此传入到是当前对象本身而不是副本,因此会发生多态调用了。

注意:

我们如上讨论的主要问题就是by-value传递会执行很多的构造与析构过程,而by-reference传递会很好地解决这个问题。但是并不是所有类型的变量都适合by-reference传递。比如内置类型、STL迭代器、函数对象,对它们而言,by-value传递往往比较合适,并且效率高些。

条款20:以const-reference传递替换by-value传递的更多相关文章

  1. 条款20:宁以pass-by-reference-to-const替换pass-by-value

    本条款的要点: 1.尽量以pass-by-reference-to-const替换pass-by-value.前者更高效且可以避免切割问题. 2.这条规则并不适用于内建类型及STL中的迭代器和函数对象 ...

  2. 《Effective C++》——条款20:宁以pass-by-reference-to-const替换pass-by-value

    切割(slicing)问题 请看下面代码: class Window { public: ... std::string name()const; //返回窗口名称 virtual void disp ...

  3. Effective C++ -----条款20:宁以pass-by-reference-to-const替换pass-by-value Prefer pass-by-reference-to-const to pass-by-value

    尽量以pass-by-reference-to-const替换pass-by-value.前者通常比较高校,并可避免切割问题(slicing problem). 以上规则并不适用于内置类型,以及STL ...

  4. 条款20:宁以pass-by-reference-to-const替换pass-by-value(Prefer pass-by-reference-to-const to pass-by-value)

    NOTE: 1.尽量以pass-by-reference-to-const 替换pass-by-value.前者通常比较高效,并可避免切割问题(slicing problem). 2.以上规则并不适用 ...

  5. 读书笔记_Effective_C++_条款二:尽量以const, enum, inline替换#define

    其实这个条款分成两部分介绍会比较好,第一部分是用const和enum替换不带参的宏,第二部分是用inline替换带参的宏. 第一部分:用const和enum替换不带参宏 宏定义#define发生在预编 ...

  6. EC读书笔记系列之11:条款20、21

    条款20 宁以pass-by-reference-to-const替换pass-by-value 记住: ★尽量以pass-by-reference-to-const替换pass-by-value.前 ...

  7. 《MORE EFFECTIVE C++》条款20 条款21

    条款20 协助编译器实现返回值优化 当重载运算符的时候,比如+ - * / 这类运算符,该函数返回的值一定是个右值(即不能是引用),那么执行一次运算的开销可能会在临时对象上调用多次构造函数和析构函数, ...

  8. Book. Effective C++ item2-尽量使用const, enum, inline替换#define

    ##常规变量 c++里面的#define后面的定义部分,是不算代码的一部分的.所以如果你使用#define: #define ASPECT_RATIO 1.653 你希望这个代号ASPECT RATI ...

  9. Java 为值传递而不是引用传递

    ——reference Java is Pass by Value and Not Pass by Reference 其实这个问题是一个非常初级的问题,相关的概念初学者早已掌握,但是时间长了还是容易 ...

  10. JavaScript传递变量:值传递?引用传递?

    今天在看 seajs-2.2.1/src/util-events.js源码,里面有段代码不是很理解: var events = data.events = {} // Bind event seajs ...

随机推荐

  1. windows异常调用顺序

    (一) 发生异常时系统的处理顺序(by Jeremy Gordon, Hume): 1.系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正在被调试,则系统     挂 ...

  2. OS版本调研

    1引言 1.1 编写目的 本文的主要目的是通过对当前项目中使用的各种版本的操作系统进行比较,分析各自特性和稳定程度,最终推荐合适的版本作为当前的标准系统. 1.2 背景 当前,部门负责管理维护的现网使 ...

  3. CSS常用布局整理(二)

    1-2-1单列变宽布局 side列定宽300px,content列变宽,尺寸是100%-300px.核心的问题就是浮动列的宽度应该等于“100% - 300px”,而CSS显然不支持这种带有减法运算的 ...

  4. 2016-03-15:关于VS中模块定义文件

    1 def模块定义文件 在使用开源库libx265时,因x265项目的头文件x265中有如下的宏定义 #ifdef X265_API_IMPORTS #define X265_API __declsp ...

  5. ostack

  6. 做权限树时 使用EasyUI中Tree

    符合EasyUI中Tree的Json格式,我们先看一下,格式是如何的 [{ "id":1, "text":"My Documents", & ...

  7. ASP.NET的SEO: 服务器控件背后——SEO友好的Html和JavaScript

    本系列目录 假设你需要从一个页面转向其他页面,下面有很多种方式,你是如何选择的呢?你能清晰的说明理由么?         <%--链接的表现形式--%>        <asp:Hy ...

  8. [微信开发利器]微信内移动前端开发抓包调试工具fiddler使用教程

    [微信开发利器]微信内移动前端开发抓包调试工具fiddler使用教程   在朋友圈看到一款疯转的H5小游戏,想要copy,什么?只能在微信里打开?小样,图样图森破,限制了oauth.微信浏览器内打开, ...

  9. Ubuntu 14.04下java开发环境的搭建--1--JDK的安装

    说明:以下内容均是本人个人经验,接触ubuntu系统是从10.04开始,转眼转眼之间已经四年了,经常浏览各种相关论坛,发现从我刚开始基础到现在,论坛上还有很多人在问关于JAVA环境配置的相关问题.所以 ...

  10. kettle教程(1) 简单入门、kettle简单插入与更新。打开kettle

    本文要点:Kettle的建立数据库连接.使用kettle进行简单的全量对比插入更新:kettle会自动对比用户设置的对比字段,若目标表不存在该字段,则新插入该条记录.若存在,则更新.   Kettle ...