设计模式之单例类——如何让一个类只实例化一个对象
2016-03-31 22:17
351 查看
其实这是一道面试题。
这道题的思路是,我只让这个类通过一个公有函数实例化对象,即可以将构造函数设置为私有成员函数。
这是我第一次写出的代码,我本来认为这样就可以了,结果经过我的调试,发现这样写是有问题的。
通过上面的图片可以看出,我们实例化的a的地址,跟在SingleCreate函数中返回的m的值是不同的,即这个类实例化了两个对象,这样就偏题了。
经过分析,我们现在来看SingleCreate函数
这里retuen 的*m,会自动生成拷贝构造函数,因为return *m是返回一个对象,这里是返回临时变量,自动调用拷贝构造函数,因为我们没有定义拷贝构造函数,所以这里会自动生成一个拷贝构造函数。
所以达不到只实例化一个对象的目的
二经过上面的分析,经代码优化为
这个时候经过调试,得到下面
这个时候就得到的是一个对象。因为传指针的实质是在传地址,不会产生临时变量。
现在已经解决了在返回值时只实例化一个对象的问题
接下来看第三个问题
三、构造函数
接下来我们在main函数中加这么一句话:
这个时候会发生什么呢?
对,会实例化出另一个对象,这是因为编译器自动生成了赋值构造函数,就像上面的拷贝构造函数一样,都是编译器自动生成并调用的,那么如何防止这些情况的发生?
我的方法是在类中只声明不定义拷贝构造函数以及赋值构造函数。
即
即将拷贝构造函数和赋值运算符重载函数都变成私有的成员函数,在外不可调用,就避免了因为构造函数多实例化对象的情况。
四、通过静态局部变量
静态局部变量在GetInstance函数中被定义,初始化为0,且静态局部变量存放在内存的全局数据区。函数结束时,静态局部变量不会消失,每次该函数调用 时,也不会为其重新分配空间。它始终驻留在全局数据区,直到程序运行结束。静态局部变量的初始化与全局变量类似.如果不为其显式初始化,则C++自动为其 初始化为0。
静态局部变量与全局变量共享全局数据区,但静态局部变量只在定义它的函数中可见。
通过声明静态局部变量,但不进行初始化及赋值,那么编译器自动初始化为0,且永远不会改变,所以只会实例化一个对象。这个方法更加便捷高效。
但是上面的代码没有考虑类拷贝的问题,大家参照上面的类拷贝问题可以自己把函数加上,然后去试一试,有问题可以留言大家一起解决~
这道题的思路是,我只让这个类通过一个公有函数实例化对象,即可以将构造函数设置为私有成员函数。
include <iostream> using namespace std; class Single { public: static Single& SingleCreate() { if (m == NULL) { m = new Single(); } return *m; } ~Single() { delete m; } Single(const Single &a) { if (m == NULL) { m = new Single(); } } private: Single() {} static Single *m; }; Single* Single::m = NULL; int main() { Single a = Single::SingleCreate(); }
这是我第一次写出的代码,我本来认为这样就可以了,结果经过我的调试,发现这样写是有问题的。
通过上面的图片可以看出,我们实例化的a的地址,跟在SingleCreate函数中返回的m的值是不同的,即这个类实例化了两个对象,这样就偏题了。
经过分析,我们现在来看SingleCreate函数
static Single& SingleCreate() { if (m == NULL) { m = new Single(); } return *m; }
这里retuen 的*m,会自动生成拷贝构造函数,因为return *m是返回一个对象,这里是返回临时变量,自动调用拷贝构造函数,因为我们没有定义拷贝构造函数,所以这里会自动生成一个拷贝构造函数。
所以达不到只实例化一个对象的目的
二经过上面的分析,经代码优化为
class Single { public: static Single* SingleCreatePtr() { if (m == NULL) { m = new Single(); } return m; } ~Single() { delete m; } private: Single() {} static Single *m; }; Single* Single::m = NULL; int main() { Single *a = Single::SingleCreatePtr(); }
这个时候经过调试,得到下面
这个时候就得到的是一个对象。因为传指针的实质是在传地址,不会产生临时变量。
现在已经解决了在返回值时只实例化一个对象的问题
接下来看第三个问题
三、构造函数
接下来我们在main函数中加这么一句话:
Single *b = a;
这个时候会发生什么呢?
对,会实例化出另一个对象,这是因为编译器自动生成了赋值构造函数,就像上面的拷贝构造函数一样,都是编译器自动生成并调用的,那么如何防止这些情况的发生?
我的方法是在类中只声明不定义拷贝构造函数以及赋值构造函数。
即
class Single { public: static Single& SingleCreate() { if (m == NULL) { m = new Single(); } return *m; } static Single* SingleCreatePtr() { if (m == NULL) { m = new Single(); } return m; } ~Single() { delete m; } private: Single() {} Single(const Single &a); Single& operator = (const Single &a); static Single *m; };
即将拷贝构造函数和赋值运算符重载函数都变成私有的成员函数,在外不可调用,就避免了因为构造函数多实例化对象的情况。
四、通过静态局部变量
class Single { private: Single() //构造函数是私有的 { } public: static Single & GetInstance() { static Single instance; //局部静态变量 return instance; } };
静态局部变量在GetInstance函数中被定义,初始化为0,且静态局部变量存放在内存的全局数据区。函数结束时,静态局部变量不会消失,每次该函数调用 时,也不会为其重新分配空间。它始终驻留在全局数据区,直到程序运行结束。静态局部变量的初始化与全局变量类似.如果不为其显式初始化,则C++自动为其 初始化为0。
静态局部变量与全局变量共享全局数据区,但静态局部变量只在定义它的函数中可见。
通过声明静态局部变量,但不进行初始化及赋值,那么编译器自动初始化为0,且永远不会改变,所以只会实例化一个对象。这个方法更加便捷高效。
但是上面的代码没有考虑类拷贝的问题,大家参照上面的类拷贝问题可以自己把函数加上,然后去试一试,有问题可以留言大家一起解决~
相关文章推荐
- mongoDB——split&balance操作
- HDU 1023 Traning Problem (2) 高精度卡特兰数
- 写出一条Sql语句,取出表A中的第31条到第40条记录。表A以自动增长的ID作为主键。(注意:ID可能不是连续的)
- 2016校招阿里电面题【实习】
- 文章标题
- Linux_信号操作
- 计算几何学习笔记之基本运算
- java的自动包装机制在数组中不能使用的问题
- Android Studio提交代码到SVN
- Linux Socket 原始套接字编程
- 2024
- java_生态环境
- 硬链接和软连接
- CListCtrl控件使用方法总结
- Struts2文件上传的大小限制问题
- iOS 五种传值方式
- maven java.lang.OutOfMemoryError:PermGEn space
- WSDL文件详解
- CodeForces - 367B Sereja ans Anagrams (map)
- 一起talk C栗子吧(第一百三十三回:C语言实例--创建进程时的内存细节)