`
Riddick
  • 浏览: 642280 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

《Effective C++》条款22:尽量用"传引用"代替"传值"

阅读更多

c语言中,什么都是通过传值来实现的,c++继承了这一传统并将它作为默认方式。除非明确指定,函数的形参总是通过“实参的拷贝”来初始化的,函数的调用者得到的也是函数返回值的拷贝。

正如我在本书的导言中所指出的,“通过值来传递一个对象”的具体含义是由这个对象的类的拷贝构造函数定义的。这使得传值成为一种非常昂贵的操作。例如,看下面这个(只是假想的)类的结构:

class person {
public:
  person();                         // 为简化,省略参数
  ~person();
  ...
private:
  string name, address;
};

class student: public person {
public:
  student();                        // 为简化,省略参数
  ~student();
  ...
private:
  string schoolname, schooladdress;
};

 现在定义一个简单的函数returnstudent,它取一个student参数(通过值)然后立即返回它(也通过值)。定义完后,调用这个函数:

student returnstudent(student s) { return s; }

student plato;                      // plato(柏拉图)在
                             // socrates(苏格拉底)门下学习

returnstudent(plato);          // 调用returnstudent

 这个看起来无关痛痒的函数调用过程,其内部究竟发生了些什么呢?

 

简单地说就是:首先,调用了student的拷贝构造函数用以将s初始化为plato;然后再次调用student的拷贝构造函数用以将函数返回值对象初始化为s;接着,s的析构函数被调用;最后,returnstudent返回值对象的析构函数被调用。所以,这个什么也没做的函数的成本是两个student的拷贝构造函数加上两个student析构函数。

 

但没完,还有!student对象中有两个string对象,所以每次构造一个student对象时必须也要构造两个string对象。student对象还是从person对象继承而来的,所以每次构造一个student对象时也必须构造一个person对象。一个person对象内部有另外两个string对象,所以每个person的构造也必然伴随另两个string的构造。所以,通过值来传递一个student对象最终导致调用了一个student拷贝构造函数,一个person拷贝构造函数,四个string拷贝构造函数。当student对象被摧毁时,每个构造函数对应一个析构函数的调用。所以,通过值来传递一个student对象的最终开销是六个构造函数和六个析构函数。因为returnstudent函数使用了两次传值(一次对参数,一次对返回值),这个函数总共调用了十二个构造函数和十二个析构函数!

 

在c++编译器的设计者眼里,这是最糟糕的情况。编译器可以用来消除一些对拷贝构造函数的调用(c++标准——见条款50——描述了具体在哪些条件下编译器可以执行这类的优化工作,条款m20给出了例子)。一些编译器也这样做了。但在不是所有编译器都普遍这么做的情况下,一定要对通过值来传递对象所造成的开销有所警惕。

 

为避免这种潜在的昂贵的开销,就不要通过值来传递对象,而要通过引用:

const student& returnstudent(const student& s)
{ 
     return s; 
}

 这会非常高效:没有构造函数或析构函数被调用,因为没有新的对象被创建

 

通过引用来传递参数还有另外一个优点:它避免了所谓的“切割问题(slicing problem)”。当一个派生类的对象作为基类对象被传递时,它(派生类对象)的作为派生类所具有的行为特性会被“切割”掉,从而变成了一个简单的基类对象。这往往不是你所想要的。例如,假设设计这么一套实现图形窗口系统的类:

class window {
public:
  string name() const;             // 返回窗口名
  virtual void display() const;    // 绘制窗口内容
};

class windowwithscrollbars: public window {
public:
  virtual void display() const;
};

 每个window对象都有一个名字,可以通过name函数得到;每个窗口都可以被显示,这可以通过调用display函数实现。display声明为virtual意味着一个简单的window基类对象被显示的方式往往和价格昂贵的windowwithscrollbars对象被显示的方式不同(见条款36,37,m33)。

 

现在假设写一个函数来打印窗口的名字然后显示这个窗口。下面是一个用错误的方法写出来的函数:

// 一个受“切割问题”困扰的函数
void printnameanddisplay(window w)
{
  cout << w.name();
  w.display();
}

 想象当用一个windowwithscrollbars对象来调用这个函数时将发生什么:

windowwithscrollbars wwsb;

printnameanddisplay(wwsb);

 参数w将会作为一个windows对象而被创建(它是通过值来传递的,记得吗?),所有wwsb所具有的作为windowwithscrollbars对象的行为特性都被“切割”掉了。printnameanddisplay内部,w的行为就象是一个类window的对象(因为它本身就是一个window的对象),而不管当初传到函数的对象类型是什么。尤其是,printnameanddisplay内部对display的调用总是window::display,而不是windowwithscrollbars::display

 

解决切割问题的方法是通过引用来传递w:

// 一个不受“切割问题”困扰的函数
void printnameanddisplay(const window& w)
{
  cout << w.name();
  w.display();
}

 现在w的行为就和传到函数的真实类型一致了。为了强调w虽然通过引用传递但在函数内部不能修改,就要采纳条款21的建议将它声明为const。

 

传递引用是个很好的做法,但它会导致自身的复杂性,最大的一个问题就是别名问题,这在条款17进行了讨论。另外,更重要的是,有时不能用引用来传递对象,参见条款23。最后要说的是,引用几乎都是通过指针来实现的,所以通过引用传递对象实际上是传递指针。因此,如果是一个很小的对象——例如int——传值实际上会比传引用更高效

分享到:
评论

相关推荐

    Effective.C++.中文第二版.50条款doc文档.chm

    条款22: 尽量用“传引用”而不用“传值” 条款23: 必须返回一个对象时不要试图返回一个引用 条款24: 在函数重载和设定参数缺省值间慎重选择 条款25: 避免对指针和数字类型重载 条款26: 当心潜在的二义性 条款27: ...

    Effective C++ 中文版

    条款22: 将成员变量声明为private 条款23: 宁以non-member、non-friend替换member函数 条款24:若有所参数皆需类型转换,请为此采用non-member函数 条款25:考虑写出一个不抛异常的swap函数 5.实现 条款26:尽...

    Effective Modern C++:改善C++11和C++14的42个具体做法(中文版 + 英文版)

    作者简介 作者:(美国)迈耶斯(Scott Meyers) 迈耶斯(Scott Meyers),二十多年来,Scott Meyers的Effective C++系列书籍(包括《Effective C++》《More Effective C++》和《Effective STL》)为C++编程语言...

    Effective C++:改善程序与设计的55个具体做法(中文第三版)

    Effective C++:改善程序与设计的55个具体做法(中文第三版)亚马逊图书 放到Kindle上就可以浏览学习,因为亚马逊软件有防护功能,电脑上无法使用,切记!!!!

    EFFECTIVE C++ 条款03 尽量使用const 思维导图

    EFFECTIVE C++ 条款03 尽量使用const 思维导图 在 C++ 编程中,使用 const 关键字可以提高代码的可读性、可维护性和安全性。本文将详细介绍 EFFECTIVE C++ 的第三条款:尽量使用 const 思维导图。 一、const ...

    Effective C++ Digital Collection 140 Ways to Improve Your Programming (2018).rar

    《Effective C++ Digital Collection 140 Ways to Improve Your Programming》是C++编程领域的一本经典著作,由著名C++专家Scott Meyers撰写。这本书集合了他对C++编程的深刻理解和实践经验,旨在帮助读者提升代码...

    Effective C++ 中文带目录

    例如,使用引用传递代替值传递可以减少不必要的对象构造,而当需要返回一个临时对象时,如果返回对象过大,应当优先考虑移动语义(Move Semantics)以减少不必要的资源开销。 本书还深入讨论了C++中的一些高级特性...

    Effective STL(中文)

    尽量使用vector和string来代替动态分配的数组 条款14:使用reserve来避免不必要的重新分配 条款15:小心string实现的多样性 条款16:如何将vector和string的数据传给传统的API 条款17:使用“交换技巧”...

    C++相关书籍:C++prime, C++templates,effective C++,think in C++,Qt5,widows 程序设计。

    3. **Effective C++**:作者Scott Meyers的著作,提供了55条实践建议,帮助读者编写更高效、更安全、更易于理解和维护的C++代码。内容涵盖了对象构造与销毁、资源管理、操作符重载、继承和多态等方面。 4. **...

    C++经典书籍:Effective C++

    C++经典书籍:Effective C++、C++标准模板库实战

    More Effective C++ PDF

    《More Effective C++》是C++编程领域的一本经典著作,由Scott Meyers撰写,它提供了35个设计和编程习惯,旨在帮助开发者更高效、更深入地利用C++语言。这本书不仅适合初学者,也适合有经验的C++程序员,以提升他们...

    Effective C++跟more Effective c++

    《Effective C++》和《More Effective C++》是两本由Scott Meyers撰写的经典C++编程指南,深受程序员喜爱。这两本书深入探讨了C++编程的最佳实践和常见陷阱,帮助开发者写出更高效、更安全的代码。以下是对这两本书...

    Effictive STL CHM中文版

    条款22: 避免对set和multiset的键值进行修改 条款23: 考虑用排序的vector代替关联容器 条款24: 当效率很关键时尽量用map::insert代替map::operator 条款25: 让自己熟悉非标准的hash容器 迭代器 条款26: 尽量...

    Effective C++ & More Effective C++

    《Effective C++》和《More Effective C++》是两本由Scott Meyers撰写的经典C++编程指南,深受程序员喜爱。这些书籍深入探讨了如何利用C++语言的特性来编写更高效、更易于理解和维护的代码。以下是这两本书中涵盖的...

    effective c++和more effective c++

    1. **理解隐式类型转换和构造函数**:C++允许通过构造函数进行隐式类型转换,这可能导致意料之外的行为。应谨慎使用隐式类型转换,必要时考虑使用显式关键字。 2. **使用const来指定常量行为**:const可以用来修饰...

    effective C++ 中文高清PDF可直接打印

    《Effective C++:改善程序与设计的55个具体做法》是C++编程领域的一本经典著作,由Scott Meyers撰写。这本书分为五个部分,详细讲述了如何利用C++的特性和设计模式来编写更高效、更可靠、更易于维护的代码。以下是...

    Effective C++ More effective C++ 中文版 .chm .rar

    《Effective C++》和《More Effective C++》是两本非常经典的C++编程指南,由Scott Meyers撰写,旨在帮助开发者提升C++编程技巧和理解深度。这两本书中包含了一系列的编程实践和建议,旨在让程序员写出更高效、更...

    《Effective C++中文版:改善程序与设计的55个具体做法》第3版[PDF]

     《Effective C++中文版(第3版改善程序与设计的55个具体做法)》不是读完一遍就可以束之高阁的快餐读物,也不是用以解决手边问题的参考手册,而是需要您去反复阅读体会的,C++是真正程序员的语言,背后有着精深的...

    C++: Effective Modern C++ (C++ 11, C++ 14)

    C++: Effective Modern C++ (C++ 11, C++ 14) (guide,C Programming, HTML, Javascript, Programming,all,internet, Coding, CSS, Java, PHP Vol 1) By 作者: Paul Laurence ISBN-10 书号: 1547133244 ISBN-13 书号:...

    Effective C++ 3个版本清晰pdf

    - **指针与引用**:C++中的指针和引用是强大的工具,但不恰当的使用可能导致问题。书中解释了何时使用指针,何时使用引用,并讨论了const引用和指针的用法。 2. **C++高级特性** - **模板**:深入解析了函数模板...

Global site tag (gtag.js) - Google Analytics