条款20:宁以pass-by-reference-to-const替换pass-by-value
2009-10-30 13:59
525 查看
我们考虑以下class继承体系:
class Person
{
public:
Person();
virtual ~Person();
...
private:
string name;
string address;
};
class Student : public Person
{
public:
Student();
~Student();
...
private:
string schoolName;
string schoolAddress;
};
现在考虑以下代码,其中调用函数validateStudent,需要一个Student实参(by value)并返回它是否有效;
bool validateStudent(Student s); //函数以by value方式接受参数
Student plato;
bool platoIsOk = validateStudent(plato);
对此函数而言,参数的传递成本是“一次Student copy构造函数调用,加上一次Student析构函数调用”.
我们来具体看一下对于此函数调用到底做了哪些操作:
Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象。此外Student对象继承自Person对象,所以每次构造Student对象也必须构造一个Person对象。一个Person对象又有两个string对象在其中,因此每一次Person构造动作又需承担两个string构造工作。最终结果是:调用了一次Student copy构造函数、一次Person copy构造函数、四次string copy构造函数。当函数内的那个Student复件被销毁,每一个构造函数调用动作都需要一个对应的析构函数调用动作。因此,以by value 方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”。
这样传递的成本是相当高的,如果做下面的修改,就可以避免那些构造和析构的动作
bool validateStudent(Student &s); //函数以by-reference方式接受参数
Student plato;
bool platoIsOk = validateStudent(plato);
如果我们并不需要对传入的Student作任何改变,我们可以对上面的版本进行修订
bool validateStudent(const Student &s); //函数以by-reference-to-const方式接受参数
Student plato;
bool platoIsOk = validateStudent(plato);
另外,以by reference方式传递参数也可以避免slicing(对象切割)问题,我们来看看下面这个例子:
class Window
{
public:
...
string name() const;
virtual void display() const;
};
class WindowWithScrollBars : public Window
{
public:
...
void display() const;
};
所有的Window对象都有一个名称,你可以通过name函数取得它。所有的窗口都可以显示,你可以通过display函数完成它。display是个virtual函数,这意味简单朴素的base class Window对象的显示方式和华丽高贵的WindowWithScrollBars对象的显示方式不同。
现在我们写这样一个函数打印窗口名称,然后显示该窗口。
void printNameAndDisplay(Window w) //by value
{
cout << w.name() << endl;
w.display();
}
我们现在传递一个WindowWithScrollBars对象给该函数:
WindowWithScrollBars wwsb;
printNameAndDisplay( wwsb );
由于是passed-by-value,在printNameAndDisplay函数内无论传递过来的对象原本是什么类型,参数w就像一个Window对象。因此在printNameAndDisplay内调用display调用的总是Window::display,绝不会是WindowWithScrollBars::display。
解决切割问题的办法,就是以by reference-to-const的方式传递w;
void printNameAndDisplay(const Window &w) //by reference
{
cout << w.name() << endl;
w.display();
}
如果窥视C++编译器的底层,reference往往以指针实现出来,因此pass by reference通常意味着真正传递的是指针。
对于内置类型,pass-by-value往往比pass by reference的效率高些。
class Person
{
public:
Person();
virtual ~Person();
...
private:
string name;
string address;
};
class Student : public Person
{
public:
Student();
~Student();
...
private:
string schoolName;
string schoolAddress;
};
现在考虑以下代码,其中调用函数validateStudent,需要一个Student实参(by value)并返回它是否有效;
bool validateStudent(Student s); //函数以by value方式接受参数
Student plato;
bool platoIsOk = validateStudent(plato);
对此函数而言,参数的传递成本是“一次Student copy构造函数调用,加上一次Student析构函数调用”.
我们来具体看一下对于此函数调用到底做了哪些操作:
Student对象内有两个string对象,所以每次构造一个Student对象也就构造了两个string对象。此外Student对象继承自Person对象,所以每次构造Student对象也必须构造一个Person对象。一个Person对象又有两个string对象在其中,因此每一次Person构造动作又需承担两个string构造工作。最终结果是:调用了一次Student copy构造函数、一次Person copy构造函数、四次string copy构造函数。当函数内的那个Student复件被销毁,每一个构造函数调用动作都需要一个对应的析构函数调用动作。因此,以by value 方式传递一个Student对象,总体成本是“六次构造函数和六次析构函数”。
这样传递的成本是相当高的,如果做下面的修改,就可以避免那些构造和析构的动作
bool validateStudent(Student &s); //函数以by-reference方式接受参数
Student plato;
bool platoIsOk = validateStudent(plato);
如果我们并不需要对传入的Student作任何改变,我们可以对上面的版本进行修订
bool validateStudent(const Student &s); //函数以by-reference-to-const方式接受参数
Student plato;
bool platoIsOk = validateStudent(plato);
另外,以by reference方式传递参数也可以避免slicing(对象切割)问题,我们来看看下面这个例子:
class Window
{
public:
...
string name() const;
virtual void display() const;
};
class WindowWithScrollBars : public Window
{
public:
...
void display() const;
};
所有的Window对象都有一个名称,你可以通过name函数取得它。所有的窗口都可以显示,你可以通过display函数完成它。display是个virtual函数,这意味简单朴素的base class Window对象的显示方式和华丽高贵的WindowWithScrollBars对象的显示方式不同。
现在我们写这样一个函数打印窗口名称,然后显示该窗口。
void printNameAndDisplay(Window w) //by value
{
cout << w.name() << endl;
w.display();
}
我们现在传递一个WindowWithScrollBars对象给该函数:
WindowWithScrollBars wwsb;
printNameAndDisplay( wwsb );
由于是passed-by-value,在printNameAndDisplay函数内无论传递过来的对象原本是什么类型,参数w就像一个Window对象。因此在printNameAndDisplay内调用display调用的总是Window::display,绝不会是WindowWithScrollBars::display。
解决切割问题的办法,就是以by reference-to-const的方式传递w;
void printNameAndDisplay(const Window &w) //by reference
{
cout << w.name() << endl;
w.display();
}
如果窥视C++编译器的底层,reference往往以指针实现出来,因此pass by reference通常意味着真正传递的是指针。
对于内置类型,pass-by-value往往比pass by reference的效率高些。
相关文章推荐
- Effective C++ 条款20 宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- 读书笔记《Effective c++》 条款20 宁以pass-by-reference-toconst替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- 条款20 宁以pass-by-reference-to-const替换pass-by-value
- <<Effective c++>>读书笔记---条款20:宁以pass-by-reference-to-const替换pass-by-value
- Effective C++ -----条款20:宁以pass-by-reference-to-const替换pass-by-value Prefer pass-by-reference-to-const to pass-by-value
- Effective c++学习笔记条款20:宁以 pass-by-reference-to-const替换pass-by-value
- effective C++ 条款 20:宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以 pass-by-reference to const 替换 pass by value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value(Prefer pass-by-reference-to-const to pass-by-value)
- Effective C++读书笔记-----条款20:宁以pass-by-reference-to-const替换pass-by-value
- 《Effective C++》——条款20:宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- Effective C++条款20:宁以pass-by-reference-to-const替换pass-by-value
- 《Effect C++》学习------条款20 :宁以pass-by-reference-to-const替换pass-by-value