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

2 篇文章 0 订阅
1 篇文章 0 订阅

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值