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

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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值