以某种方式撰写你的classes,使它们延缓运算,直到那些运算结果刻不容缓被迫切需要为止。
class String{}; //string类
String s1 = "Hello";
String s2 = s1;
上述这种通过复制赋值会引起较大的成本代价,s2以s1为初值通常涉及到new操作符分配堆内存,需要调用strcpy函数复制s1的数据到s2的内存,这是eager evaluation(急式评估):只因string的复制赋值函数被调用。然而,此时s2并不需要还未被使用,不需要被赋予实际内容。
lazy evaluation做法:让s1和s2共享一个值。
数据共享的唯一危机在于其中某个字符串被修改时发生:
s2.convertToUpperCase();
//该语句的话就必须将s2内容做副本给s2私人使用了
lazy evaluation:
在真正需要之前,不用着急复制出副本,尽量拖延;
只要可以就共享使用其它值。在某些领域,常有可能永远不用提供副本了。
String s = "Homer's Iliad";
...
cout << s[3]; //调用operator[] 读取s[3]
s[3] = 'x'; //调用operator[] 写入s[3]
上述语句中,对引用而来的字符串进行读取操作是无需什么代价的,但是写入操作,可能需要先做出一个副本。
困难在于:我们无法判断调用operator[]是读还是写。
运用lazy evaluation和proxy class,我们可以延缓决定是读还是写操作,直到能做出判断。
//假设程序使用了包含许多对象的大型对象
//为保持每次执行的一致性与连贯性,它们必须被存储于一个数据库里
//每一个对象都有唯一的标识符,用来从数据库里取回对象
class LargeObject
{
public:
LargeObject(ObjectID id); //从磁盘中回存对象
const string& field1() const;
int field2() const;
double field3() const;
double string& field4() const;
double string& field5() const;
...
};
//从磁盘中回存对象的成本:
void restoreAndProcessObject(ObjectID id)
{
LargeObject object(id);
...
}
// LargeObject体积很大,获取所有的数据成本非常大,而且并非所有数据都必要
void restoreAndProcessObject(ObjectID id)
{
LargeObject object(id);
if(object.field2() == 0)
cout << "Object" << id << ":null field2.\n";
}
//其实仅需field2的值,所以获取其他字段的付出都是浪费
lazy evaluation做法:
当需要某个数据的时候,才从数据库中去读取,即“demand-paged”(按需分页)对象初始化的实现方法。
class LargeObject
{
public:
LargeObject(ObjectID id);
const string& field1() const;
int field2() const;
double field3() const;
double string& field4() const;
double string& field5() const;
...
private:
ObjectID oid;
//mutable表示在任何函数里它们都能被修改,甚至const成员函数
mutable string* field1Value;
mutable int* field2Value;
mutable double* field3Value;
mutable string* field4Valuel
...
};
LargeObject::LargeObject(ObjectID id)
:oid(id),field1Value(0),field2Value(0),field3Value(0)...
{}
const string& LargeObject::field1() const
{
if(field1Value == 0)
{
//从数据库中读取field1的值,使field1Value指向这个值
}
return *field1Value;
}
//数值应用
template<class T>
class Matrix{...}; //homogeneous matrices(同质矩阵)
Matrix<int> m1(1000,1000); //一个1000*1000的矩阵
Matrix<int> m2(1000,1000);
...
Matrix<int> m3 = m1 + m2;
//+的实现是eagar evaluation:
//计算和返回m1 与 m2的和:大规模运算且大量分配内存
//lazy evaluation做法:
//建立一个数据结构于m3中,表示m3的值是m1和m2的和
Matrix<int> m4(1000,1000);
Matrix<int> m3 = m4*m1;
lazy evaluation可以避免非必要的数值计算动作。
上述四种用途展示了lazy evaluation在许多领域的作用:可避免非必要的对象复制,可区别operator[ ]的读取与写入操作,可避免非必要的数据库读取操作,可避免非必要的数值计算动作。
但是,只有当“你的软件被要求执行某些计算,而那些计算其实可以避免”的情况下,lazy evaluation才有用处。