今天的守則是討論function中的parameter傳遞的方式,傳參數的時候可能沒想太多就直接把要用的東西傳入,今天就來看看其中可能更好的做法~
今天的守則是:
Prefer pass-by-reference-to-
const
to pass-by-value
這是什麼意思呢?
我們都知道一般function的傳參數方式,而最常見的就是pass by value。而function在接收到參數的時候,他會用收到的參數的copy來做引數的初始化,而call function的人則會接收到return value的copy;這些copy行為都是用那些物件的copy constructor來完成的。這有什麼後果呢?代表pass by value這件事背後其實花費很多操作。以下面的例子為例,首先我們有兩個class:
class Person
{
public:
Person();
virtual ~Person();
private:
std::string name;
std::string address;
};
class Student: public Person
{
public:
Student();
~Student();
private:
std::string schoolName;
std::string schoolAddress;
};
然後,有一個function,需要Student
物件的input,並去call它來用:
bool validateStudent(Student s);
Student plato;
bool platoIsOK = validateStudent(plato);
以上validateStudent()
就是一個pass by value的function,下面將plato
傳入的時候經歷了哪些操作呢?
如同前面講的,要用傳入的物件來製作copy,所以 1.Student
的copy constructor會被call起來初始化Student
物件s
,而2.在function結束return value時也會把這個s
給destroy掉─也就是callStudent
的destructor。而1跟2其中又牽涉到裡面各member的copy constructor跟destructor,例如Student
裡面的兩個string objects,另外又因Student
是繼承自Person
,所以背後還有Person
的copy constructor跟destructor!Person
裡面又有自己的兩個string
member...。全部開展來看,這個pass by value的call法可是要價6個constructor跟destructor啊!
那有什麼方法可以省下這些操作呢?那就是 pass by reference to const
啦!
延續同樣的例子,會長這樣:
bool validateStudent(const Student& s);
這就可以省去那些constructor與destructor了!這種call法就不會在function內產生新的物件。
值得注意的是我們還加了 const
,這個很重要!原本pass by value的做法,因為會在function內產生新的object,所以原本傳入的object肯定不會被改到,因為所有的改動都在它的copy上發生;因此如果要改用pass by reference,我們應該也要確保原本傳入的object不會被改動!因此要加const。
而pass by reference還有另一個好處,就是避免slicing problem。
如果一個derived class的物件被function pass by value並用base class物件接住它,那function裡面copy出來的會是base class的物件!因為它是設法會去initialize base class物件,而用的是derived class的物件,那其中derived class比base class多出來的部分就會被 "sliced off" 了。通常我們傳入derived class的物件應該不預期它變成是base class的操作。
明天我們再來深入看看"sliced-off"問題,以及又有什麼情況下,"pass by value"可能是比較好的選擇。