• 条款20:以const-reference传递替换by-value传递


    缺省情况下,C++中函数参数的传递方式为by-value。即函数都是以实际参数的副本进行传递,而函数返回的也是一个副本。考虑如下实例程序:

     1 #include <iostream>
     2 
     3 class Person
     4 {
     5 public:
     6     Person(){ cout << "Person的构造函数" << endl; }
     7     virtual ~Person(){ cout << "Person的析构函数" << endl; }
     8     Person(const Person& p){ cout << "Person的copy构造函数" << endl; }
     9 
    10 private:
    11     string name;
    12     string address;
    13 };
    14 
    15 class Student : public Person
    16 {
    17 public:
    18     Student(){ cout << "Student的构造函数" << endl; }
    19     ~Student(){ cout << "Student的析构函数" << endl; }
    20     Student(const Student& p){ cout << "Student的copy构造函数" << endl; }
    21     void setID(string id){ studentID = id; }
    22     string getID() const{ return studentID; }
    23 
    24 private:
    25     string studentID;
    26 };
    27 bool validateStudent(Student s)
    28 {
    29     return s.getID().length() != 0 ? true : false;
    30 }
    31 
    32 
    33 int main()
    34 {
    35     Student s;
    36     s.setID("123456");
    37     bool isOK = validateStudent(s);
    38     std::cout << "validateStudent(): " << isOK << std::endl;
    39 }
    
    

    现在分析一下上述函数执行流程:执行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)的问题,如下所示:

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 class Window
     6 {
     7 public:
     8     string name() const{ return "Window"; };            // 返回窗口名
     9     virtual void display(){ cout << "Display Window" << endl; };         // 显示窗口
    10 };
    11 
    12 class EXWindow : public Window
    13 {
    14 public:
    15     virtual void display(){ cout << "Display EXWindow" << endl; };
    16 };
    17 
    18 void printNameAndDisplay(Window w)
    19 {
    20     cout << "窗口名:" << w.name() << endl;
    21     w.display();
    22 }
    23 
    24 int main()
    25 {
    26     EXWindow exw;
    27     printNameAndDisplay(exw);
    28 
    29     return 0;
    30 }
    
    

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

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

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

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

     

    注意:

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

     

     

     

     

  • 相关阅读:
    kubenetes-学习
    k8s-字段
    Spring Boot集成mongodb
    synchronized关键字
    Scala手记
    Python数据结构&封装解构
    Scala基础之集合
    Scala基础之集合常用方法
    Scala(2.12)之collection基本操作
    Scala基础之集合(数组)
  • 原文地址:https://www.cnblogs.com/benxintuzi/p/4537765.html
Copyright © 2020-2023  润新知