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

来源:互联网 发布:mac拷贝文件到ntfs 编辑:程序博客网 时间:2024/06/06 09:45

首先解释一下这个标题的意思吧,就是使用引用传参来替换传值传参,因为传值传参相比传引用来说有着太多的不方便,下面我来详细说明一下理由

1.理由1.使用by value会产生很多麻烦的过程(比如调用拷贝构造函数等一系列不必要的函数),看如下代码

int n = 0;class String{public:String(){n++;cout << "String::构造函数  n="<<n << endl;}String(const String& s){n++;cout << "String::拷贝构造函数 n=" << n << endl;}virtual ~String(){n++;cout << "String::析构函数 n=" << n << endl;}};class Base{public:Base(){n++;cout << "Base::构造函数 n=" << n << endl;}Base(const Base& s):name(s.name),address(s.address){n++;cout << "Base::拷贝构造函数 n=" << n << endl;}virtual ~Base(){n++;cout << "Base::析构函数 n=" << n << endl;}private:String name;String address;};class Dervice :public Base{public:Dervice(){n++;cout << "Dervice::构造函数 n=" << n << endl;}Dervice(const Dervice& s) :Base(s), Derviceddress(s.Derviceddress), Dervicename(s.Dervicename){n++;cout << "Dervice::拷贝构造函数 n=" << n << endl;}virtual ~Dervice(){n++;cout << "Dervice::析构函数 n=" << n << endl;}private:String Dervicename;String Derviceddress;};bool validata(Dervice s){return 0;}void test(){Dervice ww;cout << "****************************************" << endl;bool n = validata(ww);}int main(){test();system("pause");return 0;}


这是一个by-value的方式传参的函数的调用validata函数。
所以在这里首先1.调用Dervice的拷贝构造函数以ww为蓝本将s初始化,同样的在validata函数结束以后s会被销毁,调用Dervice的析构函数。当然这只是大概的基本调用,事情往往没那么简单,就上面的这个例子来看,因为每个类的成员也都是一个string 类型的。

所以上面完整的调用函数是1.调用Dervice的拷贝构造函数,2.调用Base的拷贝构造函数,3.调用Base中的第一个string的拷贝构造函数,4.调用Base中的第二个string的拷贝构造函数,5.调用Dervice的第一个string的拷贝构造函数,6,调用Dervice的第二个string的拷贝构造函数。当validata调用结束以后销毁s的时候又分别调用对应的析构函数。

这样看来传值是不是非常的恶心中间要调用这么多次函数,效率相对来说肯定是比较低的,

所以如果我们以引用的方式来传参,那么将会简单的很多

bool validata(Dervice &s){return 0;}void test(){Dervice ww;cout << "****************************************" << endl;bool n = validata(ww);}

但是在这里提醒一点函数的形参应该加上const修饰

bool validata(const Dervice &s){return 0;}

上面的演示我没有加,为什么要加这个const,因为之前以值传递的方式传递过去的是ww的副本,函数内部不会对main中的ww做任何改变,改变的只是ww在validata中的副本,但是现在传递形式是引用传递,如果不声明为const的话,在validata中更改的话相当于更改了main中的ww.

这为什么要用引用而不用值传递的理由1,下面我来阐述理由二

2.使用引用传递会避免对象切割问题

首先对象切割是什么呢?

解释:我用代码来说明吧

class Base{public:virtual void fun(){cout << "Base:;fun" << endl;}Base(){}Base(Base & s){cout << "Base::拷贝构造函数" << endl;}};class Dervice :public Base{public:virtual void fun(){cout << "Dervice:;fun" << endl;}Dervice(){}Dervice(Dervice &s) :Base(s){cout << "Base::拷贝构造函数" << endl;}};void test(Dervice s){s.fun();}int main(){Dervice ww;test(ww);system("pause");return 0;}


上述就是一个对象切割的例子.

一个在继承中,当一个Dervice对象以传值的方式传递并且接受他的为形参为Base类型的话,那么这个时候Base的拷贝构造函数就会被调用,而不是Dervice的拷贝构造函数被调用,这样就会造成Dervice对象的特性全部被切割掉了,仅仅留下一个Base对象的特性,这就是对象切割,所以在Test中调用fun的话,只会调用基类对象的函数。而绝不会调用派生类的函数。

这个时候使用引用传递的方式就可以解决这个问题。

void test(Base& s){s.fun();}int main(){Dervice ww;test(ww);system("pause");return 0;}
现在传进来什么类型,w就表现哪种类型。

这就解决了对象切割的问题!

最后要说的就是:

当对象为内置类型或者STL的迭代器和函数对象,可以考虑使用传值的方式,否则的话尽量采用引用传值的方式!


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