之前学过设计模式中的单例模式,用Java代码(来自《深入浅出设计模式》)可以表示如下:
//饿汉式单例模式
public class Singleton{
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return uniqueInstance;
}
}
//懒汉式单例模式
public class Singleton{
private volatile static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance == null){
synchronized(Singleton.class){
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
这样的问题,在面试中也是常考的题。它们有什么区别,大家看代码就可以很容易得知。
最近,在看《More Effective C++》一书,上面条款17:考虑使用lazy evalution(缓式评估)。这里的lazy和上面的懒汉很相似。
懒汉模式,关键在于拖延战术,像上面的代码,只有在用到uniqueInstance的时候才去实例化。因为实例化一个对象是很耗时间和空间的,尤其对于大对象来说。
在《More Effective C++》中,主要讲了4个用途。
1 Reference Counting(引用计数)
例如:
String s1 = “Hello”;
String s2 = s1;
缓式的做法是,让s1和s2共享字符串“Hello”,只有当需要修改s1或者s1的时候才去创建s1的副本给s2。
2 区分读和写
例如:
String s = “Homer’s Iliad”;//假设s是个reference-counted字符串
.......
cout << s3[3];
s[3] = ‘x’;
如果只是读取的话,我们可以继续使用前面的变量共享,所以我们要判断操作符[]是在读还是写的环境被调用。
3 缓式取出
想象你的程序使用大型对象,其中包含很多字段。如果每次用到的话,都是把整个对象取出是不合适的做法,因为不一定每个字段都用到。缓式的做法是,我们只产生一个空的对象,当对象内的某个字段被需要了,程序才从数据库中取回对应的数据。
4 Lazy Expression Evaluation (表达式缓评估)
考虑下面的代码:
Template <class T>
Class Matrix {...};
Matrix<int> m1(1000, 1000); //一个1000*1000的矩阵
Matrix<int> m2(1000, 1000); / /一个1000*1000的矩阵
...
Matrix<int> m3 = m1 + m2;
这样的话,如果一上来就把m3的值全部计算好,也是不合适的,因为后面的代码不一定全部用到其中的每个元素。假如,后面的程序只要m3[4]呢,全部计算的结果岂不是浪费了。
关于是否要用懒汉模式,取决于实际情况,只有当“你的软件被要求执行某些计算,而那些计算其实可以避免”的情况下,lazy evaluation 才有用处。
另外,也是很重要的一点是,那些class 接口对于是否使用eager evaluation或lazy evaluation 不能露出半点蛛丝马迹。
记不清哪位大神曾经说过,大概的意思是:“硬件再快,也要考虑算法的效率”,这个主题正是体现了这个思想,尽管现实的硬件已经白菜价了。