[Effective C++ --020]宁以pass-by-reference-to-const替换pass-by-value
2014-12-15 17:44
423 查看
前言:
我们都用过C的值传递方式,那么在C++情况下,值传递是怎样工作的呢?
比如:
1.程序内部先取得i的一个副本
2.将副本传递到foo函数
3.foo返回
这些副本的都是由拷贝构造函数产出的,当参数过多或者逻辑复杂时,就可能使得值传递成为费事的操作。
第一节:用pass-by-reference-to-const替换pass-by-value
我们先看类的值传递过程:
接下来我们这么调用:
上面并不是调用的所有数据,因为我们还有类的string成员!
在每次构造的时候都会调用string的构造函数,因此除了调用一次A的构造函数和一次B的构造函数,我们还额外调用了两次string的构造函数。
另外,我们还需要调用相对应的析构函数,因此上面的代码调用次数总和为:4次构造函数+4次析构函数。
简单的一个赋值,就导致这么多次函数调用,效率是问题!
方案:我们可以使用const引用来解决这个问题。在这种方式下,任何构造函数和析构函数都不需要调用。
因为引用是直接对对象进行操作的,不存在副本之说。另外定义为const,是为了不让传递的实参被误改!
除了调用函数次数减少,使用const引用还可以带来以下的好处。
★避免对象切割问题(slicing)
用一个形象的例子来解释:
如果我们这个时候将子类对象传入foo1()函数。
在这个时候,b1在值传递的时候会被认为是一个基类对象,从而产生一个基类副本,A的拷贝构造函数被会调用,于是子类B中的特性会被全部切割,仅仅留下一个基类对象!
而解决切割问题的最好方法:就是使用const引用传值!
第二节:基本类型的传递
如果我们去探寻C++编译器的底层,就会发现:引用往往以指针实现出来,因此通过指针传递通常意味着真正传递的是指针。
因此,对于内置类型而言,值传递的效率其实比引用传递的效率高些。
对于STL的迭代器和函数对象而言,这个结果也是适用的。
◆总结
1.尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题(slicing problem);
2.在针对内置类型以及STL迭代器和函数对象时,一般还是值传递比较高效。
我们都用过C的值传递方式,那么在C++情况下,值传递是怎样工作的呢?
比如:
int foo(int x); int i; foo(i);
1.程序内部先取得i的一个副本
2.将副本传递到foo函数
3.foo返回
这些副本的都是由拷贝构造函数产出的,当参数过多或者逻辑复杂时,就可能使得值传递成为费事的操作。
第一节:用pass-by-reference-to-const替换pass-by-value
我们先看类的值传递过程:
class A { public: A() {cout << "Call A\n"; } A(const A& aCopy) { // 拷贝构造函数 a = aCopy.a; } virtual ~A(){} private: string a; }; class B: public A { public: B() {cout << "call B\n"; } ~B() {} private: string b; }; void foo(B b) { return; }
接下来我们这么调用:
B b1; // 会直接先调用A的构造函数 // 再调用B的构造函数 foo(b1); // 副本调用A的构造函数 // 再调用B的构造函数
上面并不是调用的所有数据,因为我们还有类的string成员!
在每次构造的时候都会调用string的构造函数,因此除了调用一次A的构造函数和一次B的构造函数,我们还额外调用了两次string的构造函数。
另外,我们还需要调用相对应的析构函数,因此上面的代码调用次数总和为:4次构造函数+4次析构函数。
简单的一个赋值,就导致这么多次函数调用,效率是问题!
方案:我们可以使用const引用来解决这个问题。在这种方式下,任何构造函数和析构函数都不需要调用。
void foo(const B& b) { return; }
因为引用是直接对对象进行操作的,不存在副本之说。另外定义为const,是为了不让传递的实参被误改!
除了调用函数次数减少,使用const引用还可以带来以下的好处。
★避免对象切割问题(slicing)
用一个形象的例子来解释:
class A { public: A() {cout << "Call A\n"; } A(const A& aCopy) { a = aCopy.a; } virtual ~A(){} private: string a; }; class B: public A { public: B() {cout << "call B\n"; } ~B() {} private: string b; }; void foo1(A a) { return; }
如果我们这个时候将子类对象传入foo1()函数。
B b1; foo1(b1); // 子转父
在这个时候,b1在值传递的时候会被认为是一个基类对象,从而产生一个基类副本,A的拷贝构造函数被会调用,于是子类B中的特性会被全部切割,仅仅留下一个基类对象!
而解决切割问题的最好方法:就是使用const引用传值!
void foo1(const A& a) { // 不再调用A的拷贝构造函数! return; }
第二节:基本类型的传递
如果我们去探寻C++编译器的底层,就会发现:引用往往以指针实现出来,因此通过指针传递通常意味着真正传递的是指针。
因此,对于内置类型而言,值传递的效率其实比引用传递的效率高些。
对于STL的迭代器和函数对象而言,这个结果也是适用的。
◆总结
1.尽量以pass-by-reference-to-const替换pass-by-value。前者通常比较高效,并可避免切割问题(slicing problem);
2.在针对内置类型以及STL迭代器和函数对象时,一般还是值传递比较高效。
相关文章推荐
- Effective C++之‘宁以pass-by-reference-to-const替换pass-by-value’
- Effective C++ 条款20 宁以pass-by-reference-to-const替换pass-by-value
- Effective C++之‘宁以pass-by-reference-to-const替换pass-by-value’
- 宁以pass-by-reference-to-const替换pass-by-value——effective c++学习笔记
- Effective C++:条款20:宁以 pass-by-reference-to-const替换pass-by-value
- 读书笔记《Effective c++》 条款20 宁以pass-by-reference-toconst替换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
- effective C++ 条款 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
- (小结)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(Prefer pass-by-reference-to-const to pass-by-value)
- Effective C++ Item 20 宁以pass-by-reference-to-const替换pass-by-value
- 条款20:宁以pass-by-reference-to-const替换pass-by-value
- 尽量用pass-by-reference-to-const(const引用)替换pass-by-value(传值)
- 条款20:宁以pass-by-reference-to-const替换pass-by-value