您的位置:首页 > 编程语言 > C语言/C++

C++之宁以pass-by-reference-to-const替换pass-by-value(20)---《Effecitive C++》

2017-07-22 16:23 471 查看

条款20:宁以pass-by-reference-to-const替换pass-by-value

缺省情况下C++以by value方式(一个继承自C的方式)传递对象至(或来自)函数。除非你另外指定,否则函数参数都是以实际实参的复件为初值,调用端获得的亦是函数返回值的一个复件。复件(副本)的产生需要花费时间,浪费资源,参看如下代码。

#include <iostream>
#include <string>
using namespace std;
class Base{
public:
Base(){
cout << "=====Base的构造函数=====" << endl;
}
Base(int aa, int bb) :a(aa), b(bb){
cout << "=====Base的构造函数=====" << endl;
}
virtual void show()const{
cout << a << " " << b << endl;
}
virtual ~Base(){
cout << "=====Base的析构函数=====" << endl;
}
private:
int a;
int b;
};
class Derived :public Base{
public:
Derived(){
cout << "=====Derive的构造函数=====" << endl;
}
Derived(int a, int b):Base(a,b){
cout << "=====Derive的构造函数=====" << endl;
}
virtual void show()const {
cout << "子类Derived函数中的show()" << endl;
Base::show();
}
virtual ~Derived(){
cout << "=====Derive的析构函数=====" << endl;
}
};
void hello(Base b){
cout << endl;
cout << "=====hello=====" << endl;
b.show();
cout &l
13c72
t;< "---------------" << endl;
}

int main(){
Base b(1, 2);
hello(b);
return 0;
}


运行结果如下:



可以发现Base析构函数执行了两次,其实其构造函数也执行了两次,因为通过值传递的过程中,我们可以发现,没进行一次值的copy就进行了一次值的copy过程,因此,在函数结束的时候析构函数自动执行,需要析构两个函数,因此需要析构两个对象,因此出现了两个析构函数;

可以看到上面的这种行为需要对参数进行拷贝,浪费资源,同时又降低了程序执行的效率!

那么我们有什么方法可以避免这种问题呢? 当然有的,就是pass by reference-to-const: 这种方式有两种优点:

1)极大提高了程序的执行效率,没有任何构造函数或者析构函数被调用,因为没有任何新对象被创建,一般我们传递的时候应该声明为const变量,这样会保证传递进去的引用变量不会被修改; 2)用pass-by-reference凡是传递参数也可以避免slicing(对象切割)问题。当一个derived class对象通过pass-by-name方式传递的时候被视为一个base class对象,base class的copy构造函数会被调用,导致“对象的行为向derived class对象”的那些特性全被切割掉了,仅仅留下一个base class对象,这种坑让程序开发者陷入深入思考却不知道错在哪里,因此我们推荐使用pass-by-reference-const进行参数传递,参看如下代码:

#include <iostream>
#include <string>
using namespace std;
class Base{
public:
Base(){

}
Base(int aa, int bb) :a(aa), b(bb){
}
virtual void show()const{
cout << a << " " << b << endl;
}
virtual ~Base(){
}
private:
int a;
int b;
};
class Derived :public Base{
public:
Derived(){

}
Derived(int a, int b):Base(a,b){
}
virtual void show()const {
cout << "子类Derived函数中的show()" << endl;
Base::show();
}
virtual ~Derived(){
}
};
void hello(Base b){
cout << endl;
cout << "=====hello=====" << endl;
b.show();
cout << "---------------" << endl;
}
void hello2(const Base& b){
cout << endl;
cout << "=====hello2=====" << endl;
b.show();
cout << "---------------" << endl;
}
void hello3(Base* b){
cout << "=====hello3=====" << endl;
b->show();
cout << "---------------" << endl;
}
int main(){
Derived d(1, 2);
hello(d);
hello2(d);
Base* b = new Derived(2, 3);
hello3(b);
delete b;
return 0;
}


运行结果:



这里有一个逻辑坑,内置类型一般通过pass-by-value传递,因为pass-by-reference实际是通过指针传递的,对于内置类型而言,pass-by-value显然效率高一些,同时,内置类型的结构比较小,因此有的人想当然的认为小型types都是pass-by-value传递,甚至它们是用户自定义的class也无所谓,这个结论是错误的!因为有不同的类,同时编译器有不同的工作方式,因此,明智的选择是尽量采用pass-by-reference-const传递,但有特例,对于内置类型、STL的迭代器和函数对象,使用pass-by-value可能效率高一些!

总结:

1)尽量使用pass-by-reference-to-const替换pass-by-value,前者通常比较高效率,并可以避免对象切割问题;

2)以上规则并不适用于内置类型、STL迭代器和函数对象,对他们而言,pass-by-reference往往比较适当!

PS:

1、C++内置类型

算数类型:字符、整数、bool、浮点;

空类型:void。

2、C++STL迭代器:

包括Input iterator、Output iterator、Forward iterator、Bidirectional iterator、Random access iterator这5中迭代器。

3、C++函数对象:

class A{
public:
A();
...
int operator()(int x){
return x;
}
private:
int x;
};
A a(5);
a(10);


此时,a(10)就是函数对象,类似于这种的都是函数对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐