Effective C++之条款25、26

条款25:考虑写出一个不抛出异常的swap

    swap是个有趣的函数。原本他只是STL的一部分。而后成为异常安全性编程(见条款29)的脊柱,以及用来处理自我赋值可能性(见条款11)的一个常见机制。STL中swap的实现如下:

namespace std {
	template<typename T>
	void swap(T& a, T& b) {
		T temp(a);
		a = b;
		b = temp;
	}
}

    上述代码要求类型T支持拷贝构造 和赋值操作符。该缺省swap需要执行1次拷贝构造和2次赋值操作,有时候速度较慢。

    以pimpl手法设计Widget class,如下所示:

class Widget {
public:
	Widget(const Widget& rhs);
	Widget& operator=(const Widget& rhs) {
		...
		*pImpl = *(rhs.pImpl);
	}
private:
	WidgetImpl* pImpl;     //指针,所指对象内含Widget数据。
							//WidgetImpl是针对Widget class,
							//设计的class,包含很多数据成员。
};

    一旦要置换两个Widget对象值,我们只需要置换其pImpl指针,但缺省的swap算法不知道这点,它不只复制三个Widget对象,还复制三个WidgetImpl对象。非常缺乏效率。

    我们令Widget声明一个名为swap的public成员函数做真正的置换工作,然后将std::swap特化,令它调用该成员函数:

class Widget {
public:
	...
	void swap(Widget& other) {
		using std::wap;           //稍后解释
		swap(pImpl, other.pImpl); //置换pImpl指针
	}
	...
};
namespace std {
	template<>
	void swap<Widget>(Widget& a, Widget& b) {
		a.swap(b);    //调用swap成员函数
	}
}

    这种做法不只可以通过编译,还与STL容器有一致性,因为所有STL容器也都提供有public swap成员函数和std::swap特化版本。

略(后续)。。。

条款26:尽可能延后变量定义式的出现时间

    假设有这样一个函数,它计算通行密码的加密版本而后返回,前提是密码够长。如果密码太短,函数会丢出一个异常:

string encryptPassword(const string& password) {
	using namespace std;
	string encrypted;
	if(password.length() , MinimumPasswordLength) {
		throw logic_error("Password is too short");
	}
	...
	return encrypted;
}

    对象encrypted在此函数中并非完全未被使用,但如果有个异常被丢出,它就真的没被使用。也就是说如果函数encryptPassword丢出异常,你仍得付出encrypted的构造成本和析构成本。所以延后定义会更好。

string encryptPassword(const string& password) {
	using namespace std;
	if(password.length() , MinimumPasswordLength) {
		throw logic_error("Password is too short");
	}
	string encrypted;   //延后定义
	...
	return encrypted;
}

    上述encrypted调用了default构造函数和赋值操作符,如果直接对其进行copy构造则效果更好。

    变量是应该定义在循环内还是在循环外定义,然后每次循环赋值?两种写法的成本如下:

  • 做法A在循环外:1个构造函数+1个析构函数+n个赋值操作
  • 做法B在循环内 :n个构造函数+n个析构函数
        如果对象的一次赋值操作成本大于一次构造和析构的成本之和,那么选用第二种方式,否则选择第一种。但是除非(1)你知道赋值成本比“构造+析构“成本低,(2)你正在处理代码中效率高度敏感的部分,否则你应该使用B做法。

你可能感兴趣的:(学习笔记)