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

来源:互联网 发布:社交网络发展的好处 编辑:程序博客网 时间:2024/05/15 14:17

条款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 << "---------------" << 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)就是函数对象,类似于这种的都是函数对象。

阅读全文
0 0
原创粉丝点击