缺省情况下,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传递往往比较合适,并且效率高些。