【面试题002】java实现的单例模式,c++实现单例模式,实现禁止拷贝
一 c++实现单例模式
保证一个类,在一个程序当中只有一个对象,只有一个实例,这个对象要禁止拷贝,注意这里要区别于java。否者的话一个程序当中就可能出现多个对象的拷贝。
我们要禁止拷贝,需要将拷贝构造函数以及等号运算符 声明为私有的,并且呢不提供他们的实现。这样子如果我们代码里面有拷贝构造的话,编译时候会出错。
仅仅这样子是不够的,我们必须将构造函数声明为私有的,这是为了防止外部呢,任意的构造对象。
既然我们将构造函数私有化了,外部就不能通过 Singleton s1; 来定义这样一个对象。那我们就需要提供一个接口让外部呢得到这样一个对象。
Singleton.cpp:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <iostream>
#include <memory> using namespace std; class Singleton { public: /*这个GetInstance是静态的由类直接调用的*/ static Singleton *GetInstance() { /*用这种方法会出现构造出来的这个对象什么时候释放的问题*/ /* if (instacne_ == NULL) { instacne_ = new Singleton; } return instacne_;*/ /* 把裸指针用智能指针来管理 * 智能指针是重载了点号运算符的,我们访问类本身的get()方法,获得裸指针 */ if (!instacne_.get()) { instacne_ = auto_ptr<Singleton>(new Singleton); } return instacne_.get(); } ~Singleton() { cout << "~Singleton ..." << endl; } private: // 禁止拷贝--构造函数和等号运算符声明为私有的,并且不提供实现。 Singleton(const Singleton &other); Singleton &operator=(const Singleton &other); // 将构造函数说明为私有的 Singleton() { cout << "Singleton ..." << endl; } /*这实际上是一个静态的类对象,这里仅仅是引用性说明,她的定义应该在类的外边*/ static auto_ptr<Singleton> instacne_; }; /*定义性说明*/ auto_ptr<Singleton> Singleton::instacne_; |
1
2 3 4 5 6 7 8 9 10 11 12 |
int main(void)
{ //Singleton s1; //Singleton s2; Singleton *s1 = Singleton::GetInstance(); Singleton *s2 = Singleton::GetInstance(); //Singleton s3(*s1); // 调用拷贝构造函数 return 0; } |
Makefile:
1
2 3 4 5 6 7 8 9 10 11 12 |
.PHONY:clean
CPP=g++ CFLAGS=-Wall -g BIN=test OBJS=Singleton.o LIBS= $(BIN):$(OBJS) $(CPP) $(CFLAGS) $^ -o $@ $(LIBS) %.o:%.cpp $(CPP) $(CFLAGS) -c $< -o $@ clean: rm -f *.o $(BIN) |
运行结果
Singleton ...
~Singleton ...
裸指针呢,用智能指针来管理静态的一个类对象,当整个程序结束的时候,静态对象也就被销毁了,
那么静态对象的销毁就会导致这个类对象的析构函数被调用,
只不过说 智能指针释放了,静态的变量的时候会调用这个变量的析构函数
- //Singleton s1;
- //Singleton s2;
- //Singleton s3(*s1); // 调用拷贝构造函数
这些情况下都是错误的,
运行结果:
g++ -Wall -g -c Singleton.cpp -o Singleton.o
Singleton.cpp: 在函数‘int main()’中:
Singleton.cpp:36:2: 错误: ‘Singleton::Singleton(const Singleton&)’是私有的
Singleton.cpp:59:18: 错误: 在此上下文中
Singleton.cpp:57:13: 警告: 未使用的变量‘s2’ [-Wunused-variable]
make: *** [Singleton.o] 错误 1
二,实现禁止拷贝
Nocopyable这个类如何保证禁止拷贝,她的实现和Singleton类的实现差不多。
Noncopyable.cpp:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <iostream>
#include <memory> using namespace std; class Noncopyable { protected: Noncopyable() {} ~Noncopyable() {} private: Noncopyable(const Noncopyable &); const Noncopyable &operator=(const Noncopyable &); }; class Parent : private Noncopyable { public: Parent() { } Parent(const Parent &other) : Noncopyable(other) { } }; class Child : public Parent { public: //Child(const Child& other) //{ //} }; int main() { /*这两种情况都是失败的*/ //Parent p1; //Parent p2(p1); // 要调用Parent拷贝构造函数, //Parent构造函数又调用Noncopyable的拷贝构造函数 Child c1; Child c2(c1); return 0; } |
注意:
private 是实现继承,并不是为了继承她的接口
public 是接口继承
Makefile:
1
2 3 4 5 6 7 8 9 10 11 12 |
.PHONY:clean
CPP=g++ CFLAGS=-Wall -g BIN=test OBJS=Noncopyable.o LIBS= $(BIN):$(OBJS) $(CPP) $(CFLAGS) $^ -o $@ $(LIBS) %.o:%.cpp $(CPP) $(CFLAGS) -c $< -o $@ clean: rm -f *.o $(BIN) |
1
2 3 4 5 |
g++ -Wall -g -c Noncopyable.cpp -o Noncopyable.o
Noncopyable.cpp: 在复制构造函数‘Parent::Parent(const Parent&)’: Noncopyable.cpp:11:2: 错误: ‘Noncopyable::Noncopyable(const Noncopyable&)’是私有的 Noncopyable.cpp:23:49: 错误: 在此上下文中 make: *** [Noncopyable.o] 错误 1 |
这里我们需要注意的地方是,
对于构造函数来说,如果基类有默认构造函数,即使我们没有写 :Noncopyable()这句话,他也是会自动调用基类的默认构造函数的,
但是拷贝构造函数就不一样啦,如果我们没有写 :Noncopyable(other)这句话,是不会调用基类的拷贝构造函数的,
当然如果基类没有默认的构造函数,那么这个时候呢,一定要在成员列表中给出对基类构造函数的调用。
JAVA实现的单例模式:
读取配置文件,并且实例化了一个对象,这个对象保证只有一个。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package com.ebupt.ebms.conf; /** * @author zling Create on 2011-3-7 * @version 1.0 */ public class MainConfig { private String logPath = ""; // 上传的各种日志的本地路径 private static MainConfig instance = new MainConfig();//懒人模式 public static MainConfig getInstance() { if (instance == null) instance = new MainConfig(); return instance; } public String string() { StringBuffer sb = new StringBuffer(); sb.append("logPath : ").append(logPath).append(" "); return sb.toString(); } public String getLogPath() { return logPath; } public void setLogPath(String logPath) { this.logPath = logPath; } } |