Effective C++拾遗之条款20&21:值和引用双刃剑

Good Part 传引用

场景:

class Student {
public:
	string name;
	string description;
	uint32_t age;
	Student (...) {...}
	virtual print_type();
};
class MiddleSchoolStudent : public Student {
	// 实现了一种print_type()
}
bool  is_adult(Student student) {
	if (student.age >= 18) {
		student.print_type();
		return true;
	}
	return false;
}

问题:

  1. 这里is_adult函数在调用时,传递了Student的值,底层操作会调用Student的拷贝构造函数,构造两个string对象。当is_adult函数生命周期结束,这个构造出来的student会被析构掉。造成了很大的开销,这些开销在这个函数里用传引用的方式是完全可以避免的。
  2. 如果is_adult(MiddleSchoolStudent(…)) 这么做,会产生切割问题,即调用的是 Student类的 print_type() 方法,因为这就是个Student对象。

一些解释:

注意,传引用Student& student,在这里我们不涉及对student内容的修改,因此我们最好的方式是加一个const Student&,即const引用来保证传进来的对象不会被修改。这么做有两个好处:

  1. 避免构造函数析构函数的资源消耗,提升代码效率。
  2. 避免出现切割问题,保持多态性

但是有些场景不适合,可以思考下为什么不适合:传内置类型(int, double …),STL的迭代器,函数对象(这个我暂时没遇到过)

Bad Part 返回引用

场景:

复用上面的声明

Student* make_a_stack_good_student(const Student& student) {
	Student good_student(student);
	return &good_student;
}
Student* make_a_heap_good_student(const Student& student) {
	Student* good_student = new Student(student);
	return good_student;
}

问题:

首先明确我们在Good Part里传引用的原因1是说减少构造zhan的开销,但是这两个方法都无法避免构造开销。而且会造成严重问题,上面的函数命名其实已经很好的的说明了问题所在:

  1. 栈上的对象会在方法生命周期结束后销毁。空指针(很不幸,我自己工作中就写过这样的Bug,就是为了避免返回值)
  2. 堆上的对象返回引用,外面根本不知道什么时候销毁,造成内存泄露的可能性非常大。

一些解释:

虽然书上没有写,但是其实有些地方是可以返回堆上的引用,可以在此讨论下:

  1. 构造的时候,比如把复杂的构造封装到一个函数里了,保证最后能被析构。
  2. 另外可能传入参数本身就是堆上的,那封装一些对它的操作再返回这个引用也是可以的,不过这种操作最好还是封装到对象本身。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值